From de15227a1d321840ea35c6bb2d0cc01e3409e5f1 Mon Sep 17 00:00:00 2001 From: Shahar Mor Date: Thu, 17 Sep 2026 03:00:17 -0700 Subject: [PATCH 001/168] feat(terminal): search match count + Cmd+F focus parity (#9035) * feat(terminal): show search match count and keep Cmd+F from closing search Bring the terminal search bar to parity with the editor find bars: - Show a live match indicator (0/0, current/total, "No results", or + past the highlight limit) driven by the xterm SearchAddon onDidChangeResults event. - A repeat Cmd+F while the search is open now re-focuses and selects the query instead of toggling the panel closed; Esc remains the close path. Adds unit coverage for the indicator states and the toggle decision. * Use auto-generated localization key for TerminalSearch no-results (#9035) Replace the hand-written "noResults" i18n key with the SHA1-based auto key (auto.components.TerminalSearch.10e039b591) to match the repo's auto-keying convention, and sync the key across all locale catalogs. Addresses CodeRabbit review feedback. * test(terminal): cover search dispatch after keyboard module split * fix(terminal): refocus search from its input and verify real matches * test(terminal): use portable echo commands for search proof * refactor(terminal): keep search subscription and cleanup together --------- Co-authored-by: Neil --- .../src/components/TerminalSearch.test.tsx | 143 ++++++++++++++---- .../src/components/TerminalSearch.tsx | 94 +++++++----- .../terminal-pane/TerminalPaneSurface.tsx | 2 + .../expand-collapse-render-stability.test.ts | 1 + ...oard-handlers-ime-composing-chord.test.tsx | 1 + ...keyboard-handlers-ime-enter-keyup.test.tsx | 1 + .../keyboard-handlers-ime.test.tsx | 1 + .../terminal-keyboard-action-dispatch.ts | 10 +- .../terminal-keyboard-dependencies.ts | 1 + ...inal-keyboard-event-handlers-focus.test.ts | 2 + .../terminal-keyboard-event-handlers.ts | 15 ++ .../terminal-pane/terminal-keyboard-hook.ts | 3 + .../terminal-keyboard-search-dispatch.test.ts | 74 +++++++++ .../use-terminal-pane-foundation.ts | 7 + .../use-terminal-pane-global-listeners.ts | 2 + src/renderer/src/i18n/locales/en.json | 3 +- src/renderer/src/i18n/locales/es.json | 3 +- src/renderer/src/i18n/locales/ja.json | 3 +- src/renderer/src/i18n/locales/ko.json | 3 +- src/renderer/src/i18n/locales/zh.json | 3 +- tests/e2e/terminal-search-results.spec.ts | 57 +++++++ 21 files changed, 357 insertions(+), 72 deletions(-) create mode 100644 src/renderer/src/components/terminal-pane/terminal-keyboard-search-dispatch.test.ts create mode 100644 tests/e2e/terminal-search-results.spec.ts diff --git a/src/renderer/src/components/TerminalSearch.test.tsx b/src/renderer/src/components/TerminalSearch.test.tsx index f004bed4f9a..349898c11df 100644 --- a/src/renderer/src/components/TerminalSearch.test.tsx +++ b/src/renderer/src/components/TerminalSearch.test.tsx @@ -1,6 +1,6 @@ // @vitest-environment happy-dom -import { cleanup, fireEvent, render, waitFor } from '@testing-library/react' +import { act, cleanup, fireEvent, render, waitFor } from '@testing-library/react' import type { SearchAddon } from '@xterm/addon-search' import { afterEach, describe, expect, it, vi } from 'vitest' import TerminalSearch from './TerminalSearch' @@ -11,16 +11,33 @@ vi.mock('@/i18n/i18n', () => ({ afterEach(cleanup) -function createSearchAddon(): SearchAddon { - return { +type ResultsEvent = { resultIndex: number; resultCount: number } + +function createSearchAddon() { + let listener: ((payload: ResultsEvent) => void) | null = null + const dispose = vi.fn(() => { + listener = null + }) + const stub = { findNext: vi.fn(() => true), findPrevious: vi.fn(() => true), - clearDecorations: vi.fn() - } as unknown as SearchAddon + clearDecorations: vi.fn(), + onDidChangeResults: (handler: (payload: ResultsEvent) => void) => { + listener = handler + return { dispose } + } + } + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: The stub implements every addon member used by TerminalSearch. + const addon = stub as unknown as SearchAddon + return { + addon, + dispose, + emit: (payload: ResultsEvent) => act(() => listener?.(payload)) + } } -function renderSearch(searchAddon: SearchAddon): ReturnType { - return render( +function renderSearch(searchAddon: SearchAddon, query = ''): ReturnType { + const view = render( { searchStateRef={{ current: { query: '', caseSensitive: false, regex: false } }} /> ) + if (query) { + fireEvent.change(view.getByPlaceholderText('Search...'), { target: { value: query } }) + } + return view } -describe('TerminalSearch cleanup', () => { - it('clears the current addon when the query is erased', async () => { - const addon = createSearchAddon() - const view = renderSearch(addon) +describe('TerminalSearch match-count indicator', () => { + it('renders 0/0 for an empty query', () => { + const { addon } = createSearchAddon() + expect(renderSearch(addon).getByText('0/0')).toBeTruthy() + }) - fireEvent.change(view.getByPlaceholderText('Search...'), { target: { value: 'needle' } }) + it('renders current/total after a results event and updates on navigation', () => { + const stub = createSearchAddon() + const view = renderSearch(stub.addon, 'foo') + stub.emit({ resultIndex: 2, resultCount: 12 }) + expect(view.getByText('3/12')).toBeTruthy() + stub.emit({ resultIndex: 3, resultCount: 12 }) + expect(view.getByText('4/12')).toBeTruthy() + }) + + it('renders No results for a non-empty query with zero matches', () => { + const stub = createSearchAddon() + const view = renderSearch(stub.addon, 'foo') + stub.emit({ resultIndex: -1, resultCount: 0 }) + expect(view.getByText('No results')).toBeTruthy() + }) + + it('renders count+ when the highlight threshold is exceeded', () => { + const stub = createSearchAddon() + const view = renderSearch(stub.addon, 'foo') + stub.emit({ resultIndex: -1, resultCount: 1000 }) + expect(view.getByText('1000+')).toBeTruthy() + }) + + it('disposes the results subscription on unmount', () => { + const stub = createSearchAddon() + renderSearch(stub.addon, 'foo').unmount() + expect(stub.dispose).toHaveBeenCalledTimes(1) + }) +}) + +describe('TerminalSearch cleanup', () => { + it('clears the current addon and count when the query is erased', async () => { + const stub = createSearchAddon() + const { addon } = stub + const view = renderSearch(addon, 'needle') + stub.emit({ resultIndex: 1, resultCount: 3 }) await waitFor(() => expect(addon.findNext).toHaveBeenCalled()) vi.mocked(addon.clearDecorations).mockClear() vi.mocked(addon.findNext).mockClear() @@ -44,36 +101,38 @@ describe('TerminalSearch cleanup', () => { await waitFor(() => expect(addon.clearDecorations).toHaveBeenCalledTimes(1)) expect(addon.findNext).toHaveBeenCalledWith('') + expect(view.getByText('0/0')).toBeTruthy() }) - it('clears the previous addon when the search moves to another pane', async () => { - const previousAddon = createSearchAddon() - const nextAddon = createSearchAddon() - const view = renderSearch(previousAddon) - - fireEvent.change(view.getByPlaceholderText('Search...'), { target: { value: 'needle' } }) - await waitFor(() => expect(previousAddon.findNext).toHaveBeenCalled()) - vi.mocked(previousAddon.clearDecorations).mockClear() - vi.mocked(previousAddon.findNext).mockClear() + it('clears and unsubscribes the previous addon when search moves to another pane', async () => { + const previous = createSearchAddon() + const next = createSearchAddon() + const view = renderSearch(previous.addon, 'needle') + previous.emit({ resultIndex: 1, resultCount: 3 }) + await waitFor(() => expect(previous.addon.findNext).toHaveBeenCalled()) + vi.mocked(previous.addon.clearDecorations).mockClear() + vi.mocked(previous.addon.findNext).mockClear() view.rerender( ) - expect(previousAddon.clearDecorations).toHaveBeenCalledTimes(1) - expect(previousAddon.findNext).toHaveBeenCalledWith('') + expect(previous.addon.clearDecorations).toHaveBeenCalledTimes(1) + expect(previous.addon.findNext).toHaveBeenCalledWith('') + expect(previous.dispose).toHaveBeenCalledTimes(1) + next.emit({ resultIndex: 0, resultCount: 7 }) + previous.emit({ resultIndex: 2, resultCount: 3 }) + expect(view.getByText('1/7')).toBeTruthy() }) it('clears the addon when the search portal unmounts', async () => { - const addon = createSearchAddon() - const view = renderSearch(addon) - - fireEvent.change(view.getByPlaceholderText('Search...'), { target: { value: 'needle' } }) + const { addon } = createSearchAddon() + const view = renderSearch(addon, 'needle') await waitFor(() => expect(addon.findNext).toHaveBeenCalled()) vi.mocked(addon.clearDecorations).mockClear() vi.mocked(addon.findNext).mockClear() @@ -83,4 +142,32 @@ describe('TerminalSearch cleanup', () => { expect(addon.clearDecorations).toHaveBeenCalledTimes(1) expect(addon.findNext).toHaveBeenCalledWith('') }) + + it('exposes the search input for refocusing and clears its ref on close', () => { + const { addon } = createSearchAddon() + const inputRef: { current: HTMLInputElement | null } = { current: null } + const searchStateRef = { current: { query: '', caseSensitive: false, regex: false } } + const view = render( + + ) + expect(inputRef.current).toBe(view.getByPlaceholderText('Search...')) + fireEvent.change(view.getByPlaceholderText('Search...'), { target: { value: 'needle' } }) + view.rerender( + + ) + expect(inputRef.current).toBeNull() + expect(addon.findNext).toHaveBeenLastCalledWith('') + }) }) diff --git a/src/renderer/src/components/TerminalSearch.tsx b/src/renderer/src/components/TerminalSearch.tsx index ed8bf1b906a..79051f5d84b 100644 --- a/src/renderer/src/components/TerminalSearch.tsx +++ b/src/renderer/src/components/TerminalSearch.tsx @@ -12,8 +12,12 @@ type TerminalSearchProps = { onClose: () => void searchAddon: SearchAddon | null searchStateRef: React.RefObject + inputRef?: React.RefObject } +// xterm uses index -1 when results exceed its highlight limit. +const EMPTY_RESULTS = { resultIndex: -1, resultCount: 0 } + function clearTerminalSearch(searchAddon: SearchAddon | null): void { if (!searchAddon) { return @@ -27,19 +31,16 @@ export default function TerminalSearch({ isOpen, onClose, searchAddon, - searchStateRef + searchStateRef, + inputRef }: TerminalSearchProps): React.JSX.Element | null { const [query, setQuery] = useState('') const [caseSensitive, setCaseSensitive] = useState(false) const [regex, setRegex] = useState(false) + const [results, setResults] = useState(EMPTY_RESULTS) const requestQuery = getFindRequestQuery(query) - // Why: the default xterm SearchAddon highlights blend into common - // terminal backgrounds (see orca#612). Providing explicit decoration - // colors gives all matches a visible yellow background and the - // current match a brighter orange, matching the contrast VS Code and - // iTerm2 use for terminal search. xterm requires #RRGGBB format for - // the background colors. + // xterm needs hex colors; explicit highlights stay visible over terminal themes (#612). const searchOptions = useCallback( (incremental: boolean = false) => ({ caseSensitive, @@ -77,27 +78,34 @@ export default function TerminalSearch({ } }, [searchAddon, requestQuery, searchOptions]) - const handleInputRef = useCallback((input: HTMLInputElement | null): void => { - input?.focus() - }, []) - - useEffect( - () => () => { - clearTerminalSearch(searchAddon) + const handleInputRef = useCallback( + (input: HTMLInputElement | null): void => { + if (inputRef) { + inputRef.current = input + } + input?.focus() + input?.select() }, - [searchAddon] + [inputRef] ) + // One addon subscription tracks both panel and keyboard navigation. useEffect(() => { - // Keep the ref in sync so the keyboard handler (Cmd+G / Cmd+Shift+G) - // can read the current search state without lifting it to parent state. - searchStateRef.current = { query: requestQuery ?? '', caseSensitive, regex } - - if (!isOpen) { - clearTerminalSearch(searchAddon) + if (!searchAddon) { return } - if (!requestQuery) { + const disposable = searchAddon.onDidChangeResults(setResults) + return () => { + disposable.dispose() + clearTerminalSearch(searchAddon) + } + }, [searchAddon]) + + useEffect(() => { + // Global match-navigation shortcuts read the same query as the panel. + searchStateRef.current = { query: requestQuery ?? '', caseSensitive, regex } + + if (!isOpen || !requestQuery) { clearTerminalSearch(searchAddon) return } @@ -129,11 +137,19 @@ export default function TerminalSearch({ return null } + const matchStatus = !requestQuery + ? '0/0' + : results.resultCount === 0 + ? translate('auto.components.TerminalSearch.10e039b591', 'No results') + : results.resultIndex === -1 + ? `${results.resultCount}+` + : `${results.resultIndex + 1}/${results.resultCount}` + return (
setQuery(e.target.value)} placeholder={translate('auto.components.TerminalSearch.e07012f26e', 'Search...')} - className="min-w-0 flex-1 border-none bg-transparent text-sm text-white outline-none placeholder:text-zinc-500" + className="min-w-0 flex-1 border-none bg-transparent text-sm text-popover-foreground outline-none placeholder:text-muted-foreground" /> -
+ + {matchStatus} + + +
-
+
+
+
saved note body
+
+
+
+

Body text

+
+ +
+
+
graph TD;
+
` + const payload = await getActiveMarkdownExportPayload({ + fileId: '/repo/docs/readme.md', + root + }) + const exported = parseExportedHtml(payload?.html) + expect(exported.querySelector('h1')?.textContent).toBe('Title') + expect(exported.querySelector('p')?.textContent).toBe('Body text') + expect(exported.querySelector('pre code')?.textContent).toContain('graph TD;') + expect(exported.querySelector('.markdown-annotation-controls')).toBeNull() + expect(exported.querySelector('.markdown-annotation-add')).toBeNull() + expect(exported.querySelector('.markdown-annotation-composer')).toBeNull() + expect(exported.querySelector('.markdown-annotation-note-stack')).toBeNull() + expect(exported.textContent).not.toContain('draft note') + expect(exported.textContent).not.toContain('saved note body') + // Why: scrub runs on a clone; the live preview keeps its controls. + expect(root.querySelector('.markdown-annotation-controls')).not.toBeNull() + }) + + it('strips list-block annotation controls while preserving list text', async () => { + await mockPreviewOpenFile() + const root = document.createElement('div') + // Why: attr-only fixture (renamed class) proves the generic + // data-orca-export-hide rule scrubs even after a class rename. + root.innerHTML = ` +
+
    +
  • +
    + List item +
    + +
    +
    +
  • +
+
` + const payload = await getActiveMarkdownExportPayload({ + fileId: '/repo/docs/readme.md', + root + }) + const exported = parseExportedHtml(payload?.html) + expect(exported.querySelector('li')?.textContent).toContain('List item') + expect(exported.querySelector('[data-orca-export-hide]')).toBeNull() + expect(exported.querySelector('.markdown-annotation-add')).toBeNull() + expect(root.querySelector('[data-orca-export-hide]')).not.toBeNull() + }) }) + +async function mockPreviewOpenFile(): Promise { + const { useAppStore } = await import('@/store') + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: test mock provides only openFiles, the sole store slice getActiveMarkdownExportPayload reads. + vi.mocked(useAppStore.getState).mockReturnValue({ + openFiles: [ + { + id: '/repo/docs/readme.md', + filePath: '/repo/docs/readme.md', + relativePath: 'docs/readme.md', + mode: 'markdown-preview' + } + ] + } as never) +} + +function parseExportedHtml(html: string | undefined): HTMLElement { + const container = document.createElement('div') + container.innerHTML = html ?? '' + return container +} diff --git a/src/renderer/src/components/editor/markdown-export-extract.ts b/src/renderer/src/components/editor/markdown-export-extract.ts index a0ec5ebb2fe..acf53973921 100644 --- a/src/renderer/src/components/editor/markdown-export-extract.ts +++ b/src/renderer/src/components/editor/markdown-export-extract.ts @@ -17,12 +17,18 @@ const DOCUMENT_SUBTREE_SELECTOR = '.ProseMirror, .markdown-body' // Why: even after picking the smallest subtree, a few in-document UI leaks // can remain. The design doc lists these by name and treats the cloned-scrub // pass as a belt-and-suspenders defense so PDF output never shows copy -// buttons, per-block search highlights, or other transient affordances. +// buttons, per-block search highlights, annotation controls, or other +// transient affordances. const UI_ONLY_SELECTORS = [ '.code-block-copy-btn', '.markdown-preview-search', '[class*="rich-markdown-search"]', - '[data-orca-export-hide="true"]' + // Why: preview annotation controls (add-note button, composer, note stack) + // render inside `.markdown-body`. The source also carries + // `data-orca-export-hide`, so the generic rule below covers renames; this + // explicit entry covers an attr-strip regression. + '.markdown-annotation-controls', + '[data-orca-export-hide]' ] function basenameWithoutExt(filePath: string): string { diff --git a/src/renderer/src/components/editor/markdown-export-html.test.ts b/src/renderer/src/components/editor/markdown-export-html.test.ts index 66bce46fba6..9d98aa61502 100644 --- a/src/renderer/src/components/editor/markdown-export-html.test.ts +++ b/src/renderer/src/components/editor/markdown-export-html.test.ts @@ -28,4 +28,11 @@ describe('buildMarkdownExportHtml', () => { const html = buildMarkdownExportHtml({ title: '', renderedHtml: '

x

' }) expect(html).toContain('Untitled') }) + + it('hides preview annotation controls even if DOM scrubbing misses them', () => { + const html = buildMarkdownExportHtml({ title: 'Notes', renderedHtml: '

x

' }) + expect(html).toContain('.markdown-annotation-controls') + expect(html).toContain('[data-orca-export-hide') + expect(html).toContain('display: none') + }) }) diff --git a/src/renderer/src/components/editor/use-markdown-preview-annotation-renderers.tsx b/src/renderer/src/components/editor/use-markdown-preview-annotation-renderers.tsx index 4e45e337a10..ab0ffeb1146 100644 --- a/src/renderer/src/components/editor/use-markdown-preview-annotation-renderers.tsx +++ b/src/renderer/src/components/editor/use-markdown-preview-annotation-renderers.tsx @@ -88,7 +88,11 @@ export function useMarkdownPreviewAnnotationRenderers({ } return ( -
+ // Why: annotation controls (add-note button, composer, saved note + // stack) are transient review state, not document content. They render + // inside `.markdown-body`, so mark the container for PDF export + // exclusion — the extract scrub and export CSS both honor this. +
+ + +
+

+ {translate( + 'auto.components.NativeChatResumeOnRestartModal.whatIsSentTitle', + 'What Orca sends' + )} +

+

+ {translate( + 'auto.components.NativeChatResumeOnRestartModal.whatIsSentBody', + 'Continuing sends one short message to each agent, telling it that Orca restarted and asking it to check its last action before carrying on. Your own prompt is never re-sent.' + )} +

+
+ {AGENT_SESSION_RESTART_CONTINUATION_MESSAGE} +
+
+
+ + ) +} + +export function NativeChatResumeOnRestartModal(): React.JSX.Element | null { + const structuredEnabled = useAppStore( + (store) => store.settings?.experimentalStructuredNativeChat === true + ) + const launchOffer = useRef | null>(null) + const updateSettings = useAppStore((store) => store.updateSettings) + const [candidates, setCandidates] = useState([]) + /** Clock stamped when the list arrived. Row ages read against this rather than a render-time + * `Date.now()`, so they stay stable across re-renders and the render stays pure. */ + const [listedAt, setListedAt] = useState(0) + const [dontAskAgain, setDontAskAgain] = useState(false) + const [busy, setBusy] = useState(false) + const [resolved, setResolved] = useState(false) + /** + * Which of the OFFERED chats to act on. Defaults to all, and is only ever narrowed by the user. + * + * This changes which eligible chats are acted on, never what is eligible: ids are seeded from the + * host's own answer, `selectedResumeSessionIds` intersects back against it before any call, and + * the host re-derives the predicate regardless of what is sent. + */ + const [selected, setSelected] = useState>(() => new Set()) + + const toggleSelected = useCallback((sessionId: string, checked: boolean) => { + setSelected((current) => { + const next = new Set(current) + if (checked) { + next.add(sessionId) + } else { + next.delete(sessionId) + } + return next + }) + }, []) + + useEffect(() => { + if (!structuredEnabled || resolved) { + return + } + let cancelled = false + // Fetched after mount, never awaited by startup: the workspace is usable first. + const loadOffer = async (): Promise => { + // The preference belongs to this launch's request; later saves cannot dispatch another. + const autoResume = useAppStore.getState().settings?.nativeChatResumeWorkOnRestart === true + try { + const offered = await callStructuredAgentSession<{ sessions: ResumeCandidate[] }>( + LOCAL, + 'agentSession.restartResumable' + ) + if (offered.sessions.length === 0) { + return [] + } + if (autoResume) { + // Identical call to the buttons below; the host re-derives eligibility either way. + const result = await callStructuredAgentSession<{ results: RestartActionOutcome[] }>( + LOCAL, + 'agentSession.restartResume', + {} + ).catch(() => { + announceRestartUnconfirmed(offered.sessions.length, 'reconnect') + return null + }) + if (!result) { + return [] + } + // Automatic must never be silent: someone who ticked the box months ago still sees this. + announceRestartResults(allResumeSessionIds(offered.sessions), result.results, 'reconnect') + return [] + } + return offered.sessions + } catch { + // A host that cannot answer offers nothing. There is no failure worth a modal of its own. + return [] + } + } + launchOffer.current ??= loadOffer() + void launchOffer.current.then((offered) => { + if (!cancelled) { + setListedAt(Date.now()) + setCandidates(offered) + setSelected(new Set(allResumeSessionIds(offered))) + } + }) + return () => { + cancelled = true + } + }, [resolved, structuredEnabled]) + + /** Applied on whichever action the user takes, so the box means the same thing either way. */ + const persistPreference = useCallback(async (): Promise => { + if (dontAskAgain) { + await updateSettings({ nativeChatResumeWorkOnRestart: true }).catch(() => undefined) + } + }, [dontAskAgain, updateSettings]) + + const resume = useCallback( + async (sessionIds?: string[]): Promise => { + setBusy(true) + try { + void persistPreference() + const result = await callStructuredAgentSession<{ results: RestartActionOutcome[] }>( + LOCAL, + 'agentSession.restartResume', + sessionIds ? { sessionIds } : {} + ) + const settled = new Set(result.results.map((entry) => entry.sessionId)) + const remaining = candidates.filter((candidate) => !settled.has(candidate.sessionId)) + announceRestartResults( + sessionIds ?? allResumeSessionIds(candidates), + result.results, + 'reconnect' + ) + setCandidates(remaining) + // An empty result means the host settled none of them — never leave the dialog sitting open + // behind a button that did nothing. + if (remaining.length === 0 || result.results.length === 0) { + setResolved(true) + } + } catch { + announceRestartUnconfirmed( + (sessionIds ?? allResumeSessionIds(candidates)).length, + 'reconnect' + ) + setResolved(true) + } finally { + setBusy(false) + } + }, + [candidates, persistPreference] + ) + + /** + * Reconnect AND ask each agent to carry on. A deliberate action only. + * + * The automatic path calls `restartResume`, which has no send in it, so no setting — the + * checkbox included — can reach this. The checkbox opts into automatic RECONNECTION, never + * automatic continuation. + */ + const reconnectAndContinue = useCallback( + async (sessionIds: string[]): Promise => { + setBusy(true) + try { + void persistPreference() + const result = await callStructuredAgentSession<{ + continued: RestartActionOutcome[] + }>(LOCAL, 'agentSession.restartContinue', { sessionIds }) + announceRestartResults(sessionIds, result.continued, 'continue') + setResolved(true) + } catch { + announceRestartUnconfirmed(sessionIds.length, 'continue') + setResolved(true) + } finally { + setBusy(false) + } + }, + [persistPreference] + ) + + /** Any close is a decline, and a decline spends the markers so this cannot return every launch. */ + const decline = useCallback(async (): Promise => { + setResolved(true) + void persistPreference() + await callStructuredAgentSession(LOCAL, 'agentSession.restartResumableDismiss', {}).catch( + () => undefined + ) + }, [persistPreference]) + + if (!structuredEnabled || resolved || candidates.length === 0) { + return null + } + + const interruptedByUpdate = candidates.some((candidate) => candidate.trigger === 'update') + // Intersected against what the host offered, so an action can never name a chat it did not. + const chosen = selectedResumeSessionIds(candidates, selected) + + return ( + { + if (!next && !busy) { + void decline() + } + }} + > + {/* Height is capped, never the data: seeing WHICH chats would be reconnected is the whole + point, so the list scrolls inside the dialog while the header and primary action stay. */} + + + + {/* Plain wrapper owns the icon spacing; DialogTitle owns its own. */} + + + {translate( + 'auto.components.NativeChatResumeOnRestartModal.title', + 'Reconnect interrupted chats?' + )} + + + + {interruptedByUpdate + ? translate( + 'auto.components.NativeChatResumeOnRestartModal.updateBody', + 'These chats were mid-turn when Orca installed an update. Reconnecting restores each one where it stopped, with its full context and without re-sending your prompt — the interrupted reply will not continue on its own.' + ) + : translate( + 'auto.components.NativeChatResumeOnRestartModal.body', + 'These chats were mid-turn when Orca closed. Reconnecting restores each one where it stopped, with its full context and without re-sending your prompt — the interrupted reply will not continue on its own.' + )} + + {/* The true state of things is counterintuitive — the terminal sessions survived and the + chats did not — so say so where it frames the list, not as a footnote. "kept running" + rather than "were restored": nothing reconnected them, they never stopped. */} +

+ {translate( + 'auto.components.NativeChatResumeOnRestartModal.terminalSessionsUnaffected', + 'Only chats are affected — your terminal sessions kept running and need nothing from you.' + )} +

+
+ +
+ +
+ + {/* Says the quiet part: declining is not destructive, because opening the chat still + re-acquires it at the same cursor. */} +

+ {translate( + 'auto.components.NativeChatResumeOnRestartModal.notNowHint', + 'Not now keeps everything — you can reopen any chat later and carry on from the same point.' + )} +

+ + + + + + + + {/* Secondary, never the default: continuing sends a message, reconnecting does not. */} + + + + +
+
+ ) +} diff --git a/src/renderer/src/components/native-chat-restart-action-notifications.ts b/src/renderer/src/components/native-chat-restart-action-notifications.ts new file mode 100644 index 00000000000..77245fe07b3 --- /dev/null +++ b/src/renderer/src/components/native-chat-restart-action-notifications.ts @@ -0,0 +1,104 @@ +import { toast } from 'sonner' +import { translate } from '@/i18n/i18n' + +export type RestartActionOutcome = { + sessionId: string + outcome: 'resumed' | 'continued' | 'pending' | 'unknown' | 'refused' +} + +function announceResumed(count: number): void { + if (count <= 0) { + return + } + toast( + count === 1 + ? translate('auto.components.NativeChatResumeOnRestartModal.resumedOne', 'Reconnected 1 chat') + : translate( + 'auto.components.NativeChatResumeOnRestartModal.resumedMany', + 'Reconnected {{value0}} chats', + { + value0: count + } + ) + ) +} + +function announceContinued(count: number): void { + if (count <= 0) { + return + } + toast( + count === 1 + ? translate( + 'auto.components.NativeChatResumeOnRestartModal.continuedOne', + 'Reconnected 1 chat and asked it to continue' + ) + : translate( + 'auto.components.NativeChatResumeOnRestartModal.continuedMany', + 'Reconnected {{value0}} chats and asked them to continue', + { value0: count } + ) + ) +} + +export function announceRestartUnconfirmed(count: number, action: 'reconnect' | 'continue'): void { + if (count <= 0) { + return + } + toast( + action === 'continue' + ? translate( + 'auto.components.NativeChatResumeOnRestartModal.continueUnconfirmed', + 'Continuation delivery is unconfirmed for {{value0}} chats. Open them to check before sending another message.', + { value0: count, count } + ) + : translate( + 'auto.components.NativeChatResumeOnRestartModal.reconnectUnconfirmed', + 'Reconnection is unconfirmed for {{value0}} chats. You can still open them normally.', + { value0: count, count } + ) + ) +} + +export function announceRestartResults( + requested: readonly string[], + results: readonly RestartActionOutcome[], + action: 'reconnect' | 'continue' +): void { + const bySession = new Map(results.map((result) => [result.sessionId, result.outcome])) + let succeeded = 0 + let unconfirmed = 0 + let refused = 0 + for (const sessionId of new Set(requested)) { + const outcome = bySession.get(sessionId) + if (outcome === (action === 'continue' ? 'continued' : 'resumed')) { + succeeded += 1 + } else if (outcome === 'pending' || outcome === 'unknown') { + unconfirmed += 1 + } else { + // Eligibility can change after listing, so an omitted row was not acted on either. + refused += 1 + } + } + if (action === 'continue') { + announceContinued(succeeded) + } else { + announceResumed(succeeded) + } + if (refused > 0) { + toast( + action === 'continue' + ? translate( + 'auto.components.NativeChatResumeOnRestartModal.continueRefused', + '{{value0}} chats could not be continued. Open them to continue manually.', + { value0: refused, count: refused } + ) + : translate( + 'auto.components.NativeChatResumeOnRestartModal.reconnectRefused', + '{{value0}} chats could not be reconnected. You can still open them normally.', + { value0: refused, count: refused } + ) + ) + } + announceRestartUnconfirmed(unconfirmed, action) +} diff --git a/src/renderer/src/components/native-chat-resume-on-restart-grouping.test.ts b/src/renderer/src/components/native-chat-resume-on-restart-grouping.test.ts new file mode 100644 index 00000000000..42af9b3ca7e --- /dev/null +++ b/src/renderer/src/components/native-chat-resume-on-restart-grouping.test.ts @@ -0,0 +1,155 @@ +// Arranging the offer, and the one rule selection must never break. +// +// Checking a box changes WHICH eligible chats are acted on. It can never change what is eligible, +// and it can never introduce a chat the host did not offer. + +import { describe, expect, it } from 'vitest' +import { + allResumeSessionIds, + groupResumeCandidates, + groupResumeWorkspacesByRepo, + resolveResumeGroupHeader, + resumeWorkspaceKind, + selectedResumeSessionIds, + type ResumeCandidate +} from './native-chat-resume-on-restart-grouping' + +const NOW = 1_700_000_000_000 + +function candidate(overrides: Partial = {}): ResumeCandidate { + return { + sessionId: 'session-1', + workspaceId: 'repo-1::/w/one', + agent: 'codex', + trigger: 'quit', + latestPrompt: 'fix the auth bug', + recordedAt: NOW, + executionHostId: 'local', + workspaceKind: 'git-worktree', + ...overrides + } +} + +describe('selecting which offered chats to act on', () => { + it('defaults to every chat the host offered', () => { + const offered = [candidate(), candidate({ sessionId: 'session-2' })] + + expect(allResumeSessionIds(offered)).toEqual(['session-1', 'session-2']) + }) + + it('acts only on the chats that are checked', () => { + const offered = [candidate(), candidate({ sessionId: 'session-2' })] + + expect(selectedResumeSessionIds(offered, new Set(['session-2']))).toEqual(['session-2']) + }) + + // THE SAFETY RULE. A selection is intersected against the offer, so a stale or invented id cannot + // reach an action. The host re-derives the predicate regardless; this keeps the client honest too. + it('drops any selected id the host did not offer', () => { + const offered = [candidate()] + + expect( + selectedResumeSessionIds(offered, new Set(['session-1', 'session-never-offered'])) + ).toEqual(['session-1']) + }) + + it('acts on nothing when nothing is checked', () => { + expect(selectedResumeSessionIds([candidate()], new Set())).toEqual([]) + }) +}) + +describe('arranging the offer the way the sidebar does', () => { + it('groups chats by workspace in the order the host offered them', () => { + const groups = groupResumeCandidates([ + candidate({ sessionId: 'a', workspaceId: 'repo-1::/w/one' }), + candidate({ sessionId: 'b', workspaceId: 'repo-1::/w/two' }), + candidate({ sessionId: 'c', workspaceId: 'repo-1::/w/one' }) + ]) + + expect(groups.map((group) => group.workspaceId)).toEqual(['repo-1::/w/one', 'repo-1::/w/two']) + expect(groups[0]?.candidates.map((entry) => entry.sessionId)).toEqual(['a', 'c']) + }) + + it('groups workspaces under the repo each belongs to', () => { + const workspaces = groupResumeCandidates([ + candidate({ sessionId: 'a', workspaceId: 'repo-1::/w/one' }), + candidate({ sessionId: 'b', workspaceId: 'repo-2::/w/two' }), + candidate({ sessionId: 'c', workspaceId: 'repo-1::/w/three' }) + ]) + + const repoGroups = groupResumeWorkspacesByRepo(workspaces, (id) => id.split('::')[0] ?? null) + + expect(repoGroups.map((group) => group.repoId)).toEqual(['repo-1', 'repo-2']) + expect(repoGroups[0]?.workspaces).toHaveLength(2) + }) + + // Workspaces with no repo share one group rather than each inventing a header of its own. + it('collects workspaces with no repo into a single group', () => { + const workspaces = groupResumeCandidates([ + candidate({ sessionId: 'a', workspaceId: 'folder:aaa' }), + candidate({ sessionId: 'b', workspaceId: 'folder:bbb' }) + ]) + + const repoGroups = groupResumeWorkspacesByRepo(workspaces, () => null) + + expect(repoGroups).toHaveLength(1) + expect(repoGroups[0]?.repoId).toBeNull() + expect(repoGroups[0]?.workspaces).toHaveLength(2) + }) +}) + +describe('choosing the workspace glyph', () => { + // The host read the kind off the durable record, so it wins over any shape-guessing. + it('uses the kind the host recorded', () => { + expect(resumeWorkspaceKind(candidate({ workspaceKind: 'folder' }))).toBe('folder') + expect(resumeWorkspaceKind(candidate({ workspaceKind: 'git-worktree' }))).toBe('git-worktree') + }) + + // An older host sends no kind; the id space still separates the two, and it is never guessed + // from a display name. + it.each([ + ['folder:0f8f-aaa', 'folder'], + ['repo-1::/w/one', 'git-worktree'] + ] as const)('falls back to the id shape for %s', (workspaceId, expected) => { + const { workspaceKind: _dropped, ...withoutKind } = candidate({ workspaceId }) + + expect(resumeWorkspaceKind(withoutKind)).toBe(expected) + }) +}) + +describe('naming the group header', () => { + const REPO_ICON = { type: 'lucide', name: 'git-branch' } as const + const REPOS = [{ id: 'repo-1', displayName: 'orca', repoIcon: REPO_ICON }] + const GROUPS = [{ id: '4c3c3452-758b-418b-add1-0a280c8e03a0', name: 'Scratch' }] + + // THE REGRESSION. A folder workspace's repoId is `folder-workspace:` and is never + // null, so the old "repoId !== null means it is a repo" test took the repo branch, found nothing + // in the repos list, and printed the raw synthetic id — a uuid — as the header. + it('titles a folder workspace with its project group name, not the raw id', () => { + const header = resolveResumeGroupHeader( + 'folder-workspace:4c3c3452-758b-418b-add1-0a280c8e03a0', + REPOS, + GROUPS + ) + + expect(header).toEqual({ kind: 'project', name: 'Scratch' }) + expect(header.name).not.toContain('folder-workspace:') + expect(header.name).not.toContain('4c3c3452') + }) + + it('titles a git repo with its display name and keeps its own glyph', () => { + expect(resolveResumeGroupHeader('repo-1', REPOS, GROUPS)).toEqual({ + kind: 'repo', + name: 'orca', + repoIcon: REPO_ICON + }) + }) + + // An unknown project group still reads as a project, so it takes the group glyph rather than + // falling back into the repo branch. + it('still reports a project for a group it cannot find', () => { + const header = resolveResumeGroupHeader('folder-workspace:missing', REPOS, GROUPS) + + expect(header.kind).toBe('project') + }) +}) diff --git a/src/renderer/src/components/native-chat-resume-on-restart-grouping.ts b/src/renderer/src/components/native-chat-resume-on-restart-grouping.ts new file mode 100644 index 00000000000..7a6c8080d88 --- /dev/null +++ b/src/renderer/src/components/native-chat-resume-on-restart-grouping.ts @@ -0,0 +1,148 @@ +import { parseWorkspaceKey } from '../../../shared/workspace-scope' +import type { AgentSessionWorkspaceKind } from '../../../shared/agent-session-record' +import type { ExecutionHostId } from '../../../shared/execution-host' +import { projectGroupIdFromRepoId } from '../../../shared/folder-workspace-worktree' +import type { RepoIcon } from '../../../shared/repo-icon' + +/** + * The offered chats, arranged the way the sidebar arranges workspaces: project/repo, then workspace, + * then the agent sessions inside it. + * + * Pure. Every identity the rows need is resolved here or supplied by the host, so the components + * stay presentational and this can be tested without a store. + */ + +export type ResumeCandidate = { + sessionId: string + workspaceId: string + agent: 'claude' | 'codex' + trigger: 'quit' | 'update' + latestPrompt: string + recordedAt: number + /** Optional on the wire: an older host omits them, and a row must still render. */ + executionHostId?: ExecutionHostId + workspaceKind?: AgentSessionWorkspaceKind + model?: string +} + +export type ResumeWorkspaceGroup = { + workspaceId: string + candidates: ResumeCandidate[] +} + +export type ResumeRepoGroup = { + /** The repo these workspaces belong to, or null for workspaces with no repo (folder workspaces). */ + repoId: string | null + workspaces: ResumeWorkspaceGroup[] +} + +/** + * The same id space automation dispatch resolves: a folder workspace by its full `folder:` + * key, a git worktree by its bare `repoId::path` id. + */ +export function isFolderWorkspaceId(workspaceId: string): boolean { + return parseWorkspaceKey(workspaceId)?.type === 'folder' +} + +/** + * The workspace kind, preferring what the HOST recorded. + * + * The host read it off the durable record, which is authoritative; the id shape is the fallback for + * an older host that sent no kind. Never inferred from a display name. + */ +export function resumeWorkspaceKind(candidate: ResumeCandidate): AgentSessionWorkspaceKind { + return ( + candidate.workspaceKind ?? + (isFolderWorkspaceId(candidate.workspaceId) ? 'folder' : 'git-worktree') + ) +} + +/** Groups by workspace, preserving the order the host offered them so the list is stable. */ +export function groupResumeCandidates( + candidates: readonly ResumeCandidate[] +): ResumeWorkspaceGroup[] { + const groups = new Map() + for (const candidate of candidates) { + const existing = groups.get(candidate.workspaceId) + if (existing) { + existing.push(candidate) + } else { + groups.set(candidate.workspaceId, [candidate]) + } + } + return [...groups].map(([workspaceId, entries]) => ({ workspaceId, candidates: entries })) +} + +/** + * Groups the workspaces under the repo each belongs to, in first-seen order. + * + * `repoIdFor` comes from the store; workspaces it cannot place collapse into a single `null` group + * rather than each inventing a header of its own. + */ +export function groupResumeWorkspacesByRepo( + workspaces: readonly ResumeWorkspaceGroup[], + repoIdFor: (workspaceId: string) => string | null +): ResumeRepoGroup[] { + const groups = new Map() + for (const workspace of workspaces) { + const repoId = repoIdFor(workspace.workspaceId) + const key = repoId ?? '\0none' + const existing = groups.get(key) + if (existing) { + existing.workspaces.push(workspace) + } else { + groups.set(key, { repoId, workspaces: [workspace] }) + } + } + return [...groups.values()] +} + +export type ResumeGroupHeader = + | { kind: 'repo'; name: string; repoIcon: RepoIcon | null } + | { kind: 'project'; name: string } + +/** + * What the top tier of a group is called, and which glyph it takes. + * + * A folder workspace's synthetic worktree carries a `repoId` of `folder-workspace:` + * — NEVER null — so "has no git repo" cannot be detected by testing for absence. Unwrapping the id + * is the only thing that separates the two, and a project group is then titled by its own name, as + * the sidebar titles it. Falling back to the raw id would print a uuid at the user. + */ +export function resolveResumeGroupHeader( + repoId: string | null, + repos: readonly { id: string; displayName: string; repoIcon?: RepoIcon | null }[], + projectGroups: readonly { id: string; name: string }[] +): ResumeGroupHeader { + const projectGroupId = projectGroupIdFromRepoId(repoId) + if (projectGroupId !== null) { + const group = projectGroups.find((entry) => entry.id === projectGroupId) + return { kind: 'project', name: group?.name ?? projectGroupId } + } + const repo = repos.find((entry) => entry.id === repoId) + return { + kind: 'repo', + name: repo?.displayName ?? repoId ?? '', + repoIcon: repo?.repoIcon ?? null + } +} + +/** Every offered session id, which is the default selection and the ceiling on any selection. */ +export function allResumeSessionIds(candidates: readonly ResumeCandidate[]): string[] { + return candidates.map((candidate) => candidate.sessionId) +} + +/** + * Narrows a selection to sessions the host actually offered. + * + * Selection changes only WHICH eligible chats are acted on, never what is eligible, so anything not + * in the offered set is dropped here before it can reach an action. + */ +export function selectedResumeSessionIds( + candidates: readonly ResumeCandidate[], + selected: ReadonlySet +): string[] { + return candidates + .filter((candidate) => selected.has(candidate.sessionId)) + .map((candidate) => candidate.sessionId) +} diff --git a/src/renderer/src/components/native-chat/structured-agent-session-message-projection.test.ts b/src/renderer/src/components/native-chat/structured-agent-session-message-projection.test.ts index 745aa5b4314..3a3562458cb 100644 --- a/src/renderer/src/components/native-chat/structured-agent-session-message-projection.test.ts +++ b/src/renderer/src/components/native-chat/structured-agent-session-message-projection.test.ts @@ -31,6 +31,30 @@ function item(index: number): AgentJournalRenderItem { } describe('structured agent session message projection', () => { + it('does not render a rejected host submission as a sent user message', () => { + const rejected = { ...submission(0), dispatchState: 'rejected' as const, providerItemId: null } + const refusedItem = { ...item(0), itemId: agentJournalSubmissionKey(rejected.clientMessageId) } + const acceptedItem = item(1) + expect( + projectStructuredAgentSessionMessages([refusedItem, acceptedItem], [], [rejected]) + ).toMatchObject([{ id: acceptedItem.itemId, role: 'user' }]) + }) + + it('keeps a refused local draft available through its outbox', () => { + const rejected = { ...submission(0), dispatchState: 'rejected' as const, providerItemId: null } + const refusedItem = { ...item(0), itemId: agentJournalSubmissionKey(rejected.clientMessageId) } + const draft = createStructuredAgentSessionOutboxEntry({ + clientMessageId: rejected.clientMessageId, + sessionId: 'session-1', + text: 'An unsent draft', + attachments: [], + queuedAt: 1 + }) + expect(projectStructuredAgentSessionMessages([refusedItem], [draft], [rejected])).toMatchObject( + [{ id: refusedItem.itemId, blocks: [{ text: 'An unsent draft' }] }] + ) + }) + it.each([5, 10])('renders %i rapid accepted desktop sends exactly once', (sendCount) => { const outbox = Array.from({ length: sendCount }, (_, index) => createStructuredAgentSessionOutboxEntry({ diff --git a/src/renderer/src/components/settings/NativeChatExperimentalSetting.tsx b/src/renderer/src/components/settings/NativeChatExperimentalSetting.tsx index 85d27dae2e3..20373467516 100644 --- a/src/renderer/src/components/settings/NativeChatExperimentalSetting.tsx +++ b/src/renderer/src/components/settings/NativeChatExperimentalSetting.tsx @@ -20,6 +20,7 @@ export function NativeChatExperimentalSetting({ }: NativeChatExperimentalSettingProps): React.JSX.Element { const nativeChatEnabled = settings.experimentalNativeChat === true const structuredNativeChatEnabled = settings.experimentalStructuredNativeChat === true + const resumeOnRestartEnabled = settings.nativeChatResumeWorkOnRestart === true const defaultView: NativeChatDefaultView = settings.openAgentTabsInChatByDefault === true ? 'native-chat' : 'terminal-chat' @@ -150,6 +151,36 @@ export function NativeChatExperimentalSetting({ />
) : null} + + {/* Only structured sessions have a resume cursor to continue from. */} + {defaultView === 'native-chat' && structuredNativeChatEnabled ? ( +
+
+ +

+ {translate( + 'auto.components.settings.ExperimentalPane.nativeChat.resumeCopy', + 'When Orca quits or installs an update, chats that were mid-turn are offered again on the next launch. On, they are reconnected without asking and Orca tells you afterwards — the same thing as ticking "Don\'t ask again" in that prompt. Off, you choose from the list each time. Reconnecting restores a chat where it stopped; it does not continue the interrupted reply.' + )} +

+
+ + updateSettings({ nativeChatResumeWorkOnRestart: !resumeOnRestartEnabled }) + } + /> +
+ ) : null}
) : null} diff --git a/src/renderer/src/components/sidebar/WorktreeHostContextBadge.tsx b/src/renderer/src/components/sidebar/WorktreeHostContextBadge.tsx new file mode 100644 index 00000000000..816b3bfc17b --- /dev/null +++ b/src/renderer/src/components/sidebar/WorktreeHostContextBadge.tsx @@ -0,0 +1,29 @@ +import React from 'react' + +import { Badge } from '@/components/ui/badge' +import { cn } from '@/lib/utils' + +/** + * The chip that names the machine a workspace runs on — "Local Mac", an SSH host, and so on. + * + * Extracted from the sidebar card's meta row so a second surface can show the SAME chip instead of + * growing a near-copy. The card still decides WHETHER to show it (only when the visible worktrees + * span more than one host); other surfaces may show it unconditionally. That policy deliberately + * stays with each caller — what is shared here is the appearance, not the decision. + * + * Deliberately not `DashboardHostBadge`: that one renders nothing for a local host, which is exactly + * the label this has to be able to show. + */ +export function WorktreeHostContextBadge({ + label, + className +}: { + label: string + className?: string +}): React.JSX.Element { + return ( + + {label} + + ) +} diff --git a/src/renderer/src/components/sidebar/worktree-card-meta-row.tsx b/src/renderer/src/components/sidebar/worktree-card-meta-row.tsx index 1c4e3343af5..51606140ad5 100644 --- a/src/renderer/src/components/sidebar/worktree-card-meta-row.tsx +++ b/src/renderer/src/components/sidebar/worktree-card-meta-row.tsx @@ -5,6 +5,7 @@ import { DetachedHeadBadge } from '@/components/DetachedHeadBadge' import { RepoBadgeMark } from '@/components/repo/RepoBadgeLabel' import { Badge } from '@/components/ui/badge' import CacheTimer from './CacheTimer' +import { WorktreeHostContextBadge } from './WorktreeHostContextBadge' import { CONFLICT_OPERATION_LABELS } from './WorktreeCardHelpers' import { TruncatedSidebarLabel } from './truncated-sidebar-label' import { getDirectoryName } from './worktree-card-model' @@ -55,14 +56,7 @@ export function WorktreeCardMetaRow({
)} - {showHostContextBadge && ( - - {hostContextLabel} - - )} + {showHostContextBadge && } {showIdentityInNewCard ? ( AGENT_SESSION_RESUME_MARKER_TTL_MS +} diff --git a/src/shared/default-global-settings.ts b/src/shared/default-global-settings.ts index 3ceafc5c386..6869c3fe8a0 100644 --- a/src/shared/default-global-settings.ts +++ b/src/shared/default-global-settings.ts @@ -130,6 +130,7 @@ export function buildDefaultSettings(args: { openAgentTabsInChatByDefault: false, experimentalNativeChat: false, experimentalStructuredNativeChat: false, + nativeChatResumeWorkOnRestart: false, nativeChatSessionOptions: {}, openInApplications: [...DEFAULT_OPEN_IN_APPLICATIONS], rightSidebarOpenByDefault: true, diff --git a/src/shared/folder-workspace-worktree.test.ts b/src/shared/folder-workspace-worktree.test.ts index 420da41488c..d259d180acb 100644 --- a/src/shared/folder-workspace-worktree.test.ts +++ b/src/shared/folder-workspace-worktree.test.ts @@ -1,6 +1,10 @@ import { describe, expect, it } from 'vitest' import type { FolderWorkspace } from './folder-workspace-types' -import { folderWorkspaceToWorktree } from './folder-workspace-worktree' +import { + folderWorkspaceRepoId, + folderWorkspaceToWorktree, + projectGroupIdFromRepoId +} from './folder-workspace-worktree' function makeFolderWorkspace(overrides: Partial = {}): FolderWorkspace { return { @@ -173,3 +177,36 @@ describe('folderWorkspaceToWorktree', () => { expect(gitlabMr.linkedGitLabIssue).toBeNull() }) }) + +describe('recognising a folder workspace repoId', () => { + // The defect this exists for: a folder workspace's repoId is NEVER null, so code that tests for + // absence to mean "no git repo" takes the repo branch and renders the raw synthetic id. + it('never mints a null repoId, so absence cannot be the test for having no repo', () => { + const worktree = folderWorkspaceToWorktree(makeFolderWorkspace({ projectGroupId: 'group-9' })) + + expect(worktree.repoId).not.toBeNull() + expect(projectGroupIdFromRepoId(worktree.repoId)).toBe('group-9') + }) + + it('round-trips the project group through the id it mints', () => { + expect(projectGroupIdFromRepoId(folderWorkspaceRepoId('4c3c3452-758b'))).toBe('4c3c3452-758b') + }) + + // A real git repo id must not be mistaken for a project group, or a repo would lose its own name. + // The long id is the one that DISCRIMINATES: anything shorter than the prefix slices to an empty + // string and reads as null even with no prefix check, so short fixtures alone prove nothing. + it.each([ + 'repo-1', + 'acme/app', + '', + 'folder-workspace:', + 'a-repo-id-comfortably-longer-than-the-prefix' + ])('reports no project group for %s', (repoId) => { + expect(projectGroupIdFromRepoId(repoId)).toBeNull() + }) + + it('reports no project group for an absent repoId', () => { + expect(projectGroupIdFromRepoId(null)).toBeNull() + expect(projectGroupIdFromRepoId(undefined)).toBeNull() + }) +}) diff --git a/src/shared/folder-workspace-worktree.ts b/src/shared/folder-workspace-worktree.ts index c8715a12b3a..7aec035dc6c 100644 --- a/src/shared/folder-workspace-worktree.ts +++ b/src/shared/folder-workspace-worktree.ts @@ -4,6 +4,28 @@ import { folderWorkspaceKey } from './workspace-scope' import { parseExecutionHostId, toSshExecutionHostId } from './execution-host' import { normalizeWorkspaceCreatorProvenance } from './workspace-creator-provenance' +/** + * A folder workspace has no git repo, so its synthetic `Worktree` borrows the `repoId` slot to + * name the PROJECT GROUP it belongs to. The value is never null, so a caller testing `repoId` for + * absence to detect "no repo" will be wrong for every folder workspace. + * + * Minting and recognising it live together here so the two cannot drift. + */ +const FOLDER_WORKSPACE_REPO_ID_PREFIX = 'folder-workspace:' + +export function folderWorkspaceRepoId(projectGroupId: string): string { + return `${FOLDER_WORKSPACE_REPO_ID_PREFIX}${projectGroupId}` +} + +/** The project group a synthetic repoId stands for, or null when it names a real git repo. */ +export function projectGroupIdFromRepoId(repoId: string | null | undefined): string | null { + if (typeof repoId !== 'string' || !repoId.startsWith(FOLDER_WORKSPACE_REPO_ID_PREFIX)) { + return null + } + const projectGroupId = repoId.slice(FOLDER_WORKSPACE_REPO_ID_PREFIX.length) + return projectGroupId === '' ? null : projectGroupId +} + export function folderWorkspaceToWorktree(folderWorkspace: FolderWorkspace): Worktree { const linkedTask = folderWorkspace.linkedTask const creatorProvenance = normalizeWorkspaceCreatorProvenance(folderWorkspace.creatorProvenance) @@ -13,7 +35,7 @@ export function folderWorkspaceToWorktree(folderWorkspace: FolderWorkspace): Wor const parsedHost = parseExecutionHostId(hostId) return { id: folderWorkspaceKey(folderWorkspace.id), - repoId: `folder-workspace:${folderWorkspace.projectGroupId}`, + repoId: folderWorkspaceRepoId(folderWorkspace.projectGroupId), ...(creatorProvenance ? { creatorProvenance } : {}), displayName: folderWorkspace.name, comment: folderWorkspace.comment, diff --git a/src/shared/global-settings-types.ts b/src/shared/global-settings-types.ts index 086f3dd0d17..14393ab34e1 100644 --- a/src/shared/global-settings-types.ts +++ b/src/shared/global-settings-types.ts @@ -219,6 +219,9 @@ export type GlobalSettings = { experimentalNativeChat?: boolean /** Opt-in updated structured runtime; off keeps the existing PTY-backed native chat path. */ experimentalStructuredNativeChat?: boolean + /** Opt-in: resume working structured chats automatically on the next launch. Off still offers + * the list, so the user sees exactly what would run before anything spends tokens. */ + nativeChatResumeWorkOnRestart?: boolean /** Last explicit native-chat model + option selections; live panes need an applied/dispatched record before showing a value. */ nativeChatSessionOptions?: PersistedNativeChatSessionOptions /** Extra launcher rows for the worktree "Open in" submenu. VS Code is always shown first. */ diff --git a/src/shared/rpc-contract/rpc-params-catalog.generated.ts b/src/shared/rpc-contract/rpc-params-catalog.generated.ts index 46ae6f7d27f..3168406805c 100644 --- a/src/shared/rpc-contract/rpc-params-catalog.generated.ts +++ b/src/shared/rpc-contract/rpc-params-catalog.generated.ts @@ -472,6 +472,8 @@ import { HoldParams, OptionsParams, RespondParams, + RestartResumableParams, + RestartResumeParams, RewindParams, SendParams, SetOptionParams, @@ -574,6 +576,10 @@ export const RPC_PARAMS_BY_METHOD = { 'agentSession.requestHandoff': HandoffParams, 'agentSession.respondToApproval': RespondParams, 'agentSession.respondToQuestion': RespondParams, + 'agentSession.restartContinue': RestartResumeParams, + 'agentSession.restartResumable': RestartResumableParams, + 'agentSession.restartResumableDismiss': RestartResumableParams, + 'agentSession.restartResume': RestartResumeParams, 'agentSession.reveal': OptionsParams, 'agentSession.rewind': RewindParams, 'agentSession.send': SendParams, diff --git a/src/shared/rpc-contract/structured-agent-session-params.ts b/src/shared/rpc-contract/structured-agent-session-params.ts index edbda12d6d9..8afca124482 100644 --- a/src/shared/rpc-contract/structured-agent-session-params.ts +++ b/src/shared/rpc-contract/structured-agent-session-params.ts @@ -18,6 +18,9 @@ export const MAX_BLOCKS = 64 export const MAX_OPTION_LABEL = 512 +/** One relaunch cannot offer more chats than a profile plausibly holds. */ +export const MAX_RESTART_RESUME_SESSIONS = 512 + export const SessionId = z .string() .max(MAX_ID_LENGTH) @@ -227,6 +230,16 @@ export const HoldParams = z .object({ sessionId: SessionId, holderId: Identifier('Invalid holder id') }) .strict() +/** A launch's offer to resume what the last teardown recorded as working. No arguments: the set is + * the host's to derive, never a client's to assert. */ +export const RestartResumableParams = z.object({}).strict() + +/** Omitting `sessionIds` takes the whole offered set; naming them takes that subset. Either way the + * host re-derives eligibility, so an id a client invents is simply not in the set. */ +export const RestartResumeParams = z + .object({ sessionIds: z.array(SessionId).max(MAX_RESTART_RESUME_SESSIONS).optional() }) + .strict() + export const HistoryParams = z .object({ sessionId: SessionId, diff --git a/src/shared/structured-agent-session-live-turn.ts b/src/shared/structured-agent-session-live-turn.ts index 52f3ee6a549..b53651acf1c 100644 --- a/src/shared/structured-agent-session-live-turn.ts +++ b/src/shared/structured-agent-session-live-turn.ts @@ -43,6 +43,26 @@ export function activeStructuredAgentSessionTurnIdBySequence( return newest?.state === 'running' ? newest.turnId : null } +/** The newest turn record whatever state it ended in, STATE INCLUDED. Restart resume compares both + * halves against the teardown marker: the id alone cannot tell a turn that was interrupted from + * one that finished, and offering a finished chat is the failure this feature exists to avoid. + * The running-only readers above would answer null for exactly the sessions this has to identify, + * because eviction settles them to `interrupted`. + * + * Scans backwards rather than by sequence because every caller passes a rendered snapshot, which + * is already in that order. Use the by-sequence reader above for items held unordered. */ +export function newestStructuredAgentSessionTurn( + items: readonly AgentJournalRenderItem[] +): AgentJournalTurnLifecycle | null { + for (let index = items.length - 1; index >= 0; index -= 1) { + const turn = readAgentJournalTurn(items[index]?.body) + if (turn) { + return turn + } + } + return null +} + /** * Whether the newest thing the active turn produced is the model's own reasoning. * diff --git a/src/shared/structured-agent-session-message-projection.ts b/src/shared/structured-agent-session-message-projection.ts index fe3d8d764a3..6219538fbec 100644 --- a/src/shared/structured-agent-session-message-projection.ts +++ b/src/shared/structured-agent-session-message-projection.ts @@ -14,9 +14,16 @@ export function projectStructuredAgentSessionMessages( projectItems = projectStructuredItemsToNativeChat ): NativeChatMessage[] { const optimistic = reconcileStructuredAgentSessionOutbox(outbox, submissions) - const journalled = new Set(items.map((item) => item.itemId)) + // Refused sends are ledger evidence, not conversation history; local drafts remain in the outbox. + const rejected = new Set( + submissions + .filter((submission) => submission.dispatchState === 'rejected') + .map((submission) => agentJournalSubmissionKey(submission.clientMessageId)) + ) + const visibleItems = items.filter((item) => !rejected.has(item.itemId)) + const journalled = new Set(visibleItems.map((item) => item.itemId)) return [ - ...projectItems(items), + ...projectItems(visibleItems), ...optimistic .filter((entry) => !journalled.has(agentJournalSubmissionKey(entry.clientMessageId))) .map((entry): NativeChatMessage => ({ diff --git a/src/shared/structured-agent-session-projection.ts b/src/shared/structured-agent-session-projection.ts index 68e8caf22eb..6032f5cc86e 100644 --- a/src/shared/structured-agent-session-projection.ts +++ b/src/shared/structured-agent-session-projection.ts @@ -20,7 +20,8 @@ import { sha256 } from './sha256' // Re-exported so the live-turn readers' existing consumers keep one import site. export { activeStructuredAgentSessionToolCall, - activeStructuredAgentSessionTurnId + activeStructuredAgentSessionTurnId, + newestStructuredAgentSessionTurn } from './structured-agent-session-live-turn' function boundedText(payload: { head: string; truncated: boolean; byteLength: number }): string { @@ -239,13 +240,20 @@ function messageProse(blocks: readonly NativeChatBlock[]): string { export function latestStructuredAgentSessionPrompt( items: readonly AgentJournalRenderItem[] ): string { + const body = latestStructuredAgentSessionUserItem(items)?.body + return body?.kind === 'message' ? messageProse(body.blocks) : '' +} + +export function latestStructuredAgentSessionUserItem( + items: readonly AgentJournalRenderItem[] +): AgentJournalRenderItem | null { for (let index = items.length - 1; index >= 0; index -= 1) { - const body = items[index]?.body - if (body?.kind === 'message' && body.role === 'user') { - return messageProse(body.blocks) + const item = items[index] + if (item?.body.kind === 'message' && item.body.role === 'user') { + return item } } - return '' + return null } /** The newest assistant prose in the latest user turn. Tool-only assistant items diff --git a/tests/e2e/cross-version-wire/cross-version-agent-session-wire.unit.test.ts b/tests/e2e/cross-version-wire/cross-version-agent-session-wire.unit.test.ts index ce41d778ce2..06b2a3f9248 100644 --- a/tests/e2e/cross-version-wire/cross-version-agent-session-wire.unit.test.ts +++ b/tests/e2e/cross-version-wire/cross-version-agent-session-wire.unit.test.ts @@ -16,12 +16,9 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import type { StructuredAgentSessionAdapter } from '../../../src/main/native-chat/agent-session-wire/structured-agent-session-adapter' -import { attachFingerprintFields } from '../../../src/main/native-chat/agent-session-wire/structured-agent-session-attach' -import type { AgentSessionAttachParams } from '../../../src/main/native-chat/agent-session-wire/structured-agent-session-attach' import { StructuredAgentSessionHost } from '../../../src/main/native-chat/agent-session-wire/structured-agent-session-host' import { setStructuredAgentSessionHost } from '../../../src/main/native-chat/agent-session-wire/structured-agent-session-registry' import { AgentSessionRecordStore } from '../../../src/main/runtime/agent-session-record-store' -import { computeAgentSessionPayloadFingerprint } from '../../../src/shared/agent-session-mutation-envelope' import type { AgentSessionSubscribeEvent } from '../../../src/shared/agent-session-wire' import { AGENT_SESSION_PENDING_SEND_RESULT_RUNTIME_CAPABILITY, @@ -30,7 +27,25 @@ import { STRUCTURED_AGENT_SESSION_RUNTIME_CAPABILITY } from '../../../src/shared/protocol-version' import { resolveBaselineReleaseRef } from './release-checkout' -import { structuredHostStub, turnItemSkew } from './structured-agent-session-host-fixture' +import { + installableHost, + structuredHostStub, + turnItemSkew +} from './structured-agent-session-host-fixture' +import { + attachParams, + createIntentParams, + NOW, + paramsFor, + resetOperationIds, + REWIND_METHOD, + STATUS_FEED_METHOD, + sendParams, + SESSION, + STRUCTURED_CALLS, + THREAD, + WORKSPACE +} from './structured-agent-session-surface-manifest' import { loadAgentSessionWireBuild, WORKING_TREE, @@ -42,114 +57,11 @@ import { // Why: a cold CI run extracts the baseline checkout before the first pairing. const SUITE_TIMEOUT_MS = 180_000 -const SESSION = 'session-alpha' -const WORKSPACE = 'workspace-1' -const THREAD = '019fd532-7c11-7a90-b6de-4e1a2c3d5f60' -const NOW = 1_800_000_000_000 const CLIENT_CAPABILITY_UPDATE_METHOD = 'runtime.clientCapabilities.update' -const STATUS_FEED_METHOD = 'agentSession.subscribeStatus' -const REWIND_METHOD = 'agentSession.rewind' - -/** Every method the structured surface publishes: the host method it must reach, - * and the result it must hand back. A gate that hides one method and leaks - * another is the bug; so is a method that is registered and answers with an - * error, which is why `result` is declared per method rather than inferred from - * "did not say method_not_found". `result` is omitted only where the method - * legitimately answers with no reply at all. */ -const STRUCTURED_CALLS: { - method: string - hostMethod: string | null - result?: Record -}[] = [ - { method: 'agentSession.createSupport', hostMethod: null, result: { supported: true } }, - { - method: 'agentSession.create', - hostMethod: 'attach', - result: { ok: true, replayed: false, value: { sessionId: SESSION } } - }, - { - method: 'agentSession.ensure', - hostMethod: 'attach', - result: { ok: true, replayed: false, value: { sessionId: SESSION } } - }, - { - method: 'agentSession.conversationCommand', - hostMethod: 'conversationCommand', - result: { ok: true, value: { command: 'compact', state: 'completed' } } - }, - { method: 'agentSession.send', hostMethod: 'send', result: { ok: true, replayed: false } }, - { method: 'agentSession.cancel', hostMethod: 'cancel', result: { ok: true, replayed: false } }, - { - method: REWIND_METHOD, - hostMethod: 'rewind', - result: { ok: true, replayed: false, value: { itemId: 'item-1', epoch: 'rewound-epoch' } } - }, - { method: 'agentSession.close', hostMethod: 'close', result: { ok: true } }, - { - method: 'agentSession.respondToApproval', - hostMethod: 'respondToPrompt', - result: { ok: true, replayed: false } - }, - { - method: 'agentSession.respondToQuestion', - hostMethod: 'respondToPrompt', - result: { ok: true, replayed: false } - }, - { - method: 'agentSession.setOption', - hostMethod: 'setOption', - result: { ok: true, replayed: false } - }, - { - method: 'agentSession.requestHandoff', - hostMethod: 'requestHandoff', - result: { status: { owner: 'native' } } - }, - { - method: 'agentSession.handoffStatus', - hostMethod: 'handoffStatus', - result: { owner: 'native' } - }, - { - method: 'agentSession.options', - hostMethod: 'readOptions', - result: { current: { model: 'gpt-live' } } - }, - { - method: 'agentSession.commands', - hostMethod: 'readCommands', - result: { commands: [{ name: 'clear', kind: 'command' }] } - }, - { - method: 'agentSession.reveal', - hostMethod: 'revealSession', - result: { ok: true, sessionId: SESSION, workspaceId: WORKSPACE, agent: 'codex', readable: true } - }, - { method: 'agentSession.hold', hostMethod: 'hold', result: { held: true } }, - { method: 'agentSession.release', hostMethod: 'release', result: { released: true } }, - { - method: 'agentSession.history', - hostMethod: 'history', - result: { ok: true, page: { items: [] } } - }, - // A subscription that opens with nothing to say answers with no reply at all, - // so reaching the host is the only signal that the gate opened. - { method: 'agentSession.subscribe', hostMethod: 'subscribe' }, - // The status feed opens with a snapshot of every session, so its first reply is the contract. - { - method: STATUS_FEED_METHOD, - hostMethod: 'subscribeStatus', - result: { type: 'snapshot', sessions: [] } - }, - // Teardown runs through the runtime's subscription registry rather than the - // host, so its reply is the only signal that the gate opened. - { method: 'agentSession.unsubscribe', hostMethod: null, result: { unsubscribed: true } } -] let baselineRef: string let current: AgentSessionWireBuild let baseline: AgentSessionWireBuild -let operations = 0 beforeAll(async () => { baselineRef = resolveBaselineReleaseRef() @@ -157,120 +69,6 @@ beforeAll(async () => { baseline = await loadAgentSessionWireBuild(baselineRef) }, SUITE_TIMEOUT_MS) -/** `<13-digit ms>-<32 hex>`, the only shape the durable ledger accepts. */ -function operationId(): string { - operations += 1 - return `${NOW}-${operations.toString(16).padStart(32, '0')}` -} - -function envelope(args: { - method: string - fields: Record - fence: number | null -}): Record { - return { - sessionId: SESSION, - clientOperationId: operationId(), - expectedRuntimeFence: args.fence, - payloadFingerprint: computeAgentSessionPayloadFingerprint({ - method: args.method, - sessionId: SESSION, - fields: args.fields - }) - } -} - -function attachParams(fence: number | null): Record { - const params = { - envelope: { sessionId: SESSION, clientOperationId: operationId(), expectedRuntimeFence: fence }, - location: { - executionHostId: 'local', - wslDistro: null, - workspaceId: WORKSPACE, - workspaceKind: 'git-worktree' - }, - provider: 'codex', - agent: 'codex', - accountHome: { variable: 'CODEX_HOME', path: '/home/dev/.codex' }, - runtimeKind: 'native', - providerHandle: { kind: 'codex', threadId: THREAD } - } - return { - ...params, - envelope: { - ...params.envelope, - payloadFingerprint: computeAgentSessionPayloadFingerprint({ - method: 'agentSession.attach', - sessionId: SESSION, - fields: attachFingerprintFields(params as unknown as AgentSessionAttachParams) - }) - } - } -} - -function createIntentParams(): Record { - const worktree = `id:${WORKSPACE}` - const fields = { worktree, agent: 'codex' } - return { envelope: envelope({ method: 'agentSession.create', fields, fence: null }), ...fields } -} - -function sendParams(text: string, fence: number): Record { - const body = { kind: 'message', role: 'user', blocks: [{ type: 'text', text }] } - return { envelope: envelope({ method: 'agentSession.send', fields: { body }, fence }), body } -} - -/** Schema-valid params per method; values only need to survive validation. */ -function paramsFor(method: string): unknown { - const fence = 1 - switch (method) { - case 'agentSession.createSupport': - return { worktree: `id:${WORKSPACE}`, agent: 'codex' } - case 'agentSession.create': - return createIntentParams() - case 'agentSession.ensure': - return attachParams(fence) - case 'agentSession.conversationCommand': { - const fields = { command: 'compact' } - return { envelope: envelope({ method, fields, fence }), ...fields } - } - case 'agentSession.send': - return sendParams('hi', fence) - case REWIND_METHOD: { - const fields = { itemId: 'item-1', expectedEpoch: 'current-epoch' } - return { envelope: envelope({ method, fields, fence }), ...fields } - } - case 'agentSession.cancel': - return { - envelope: envelope({ method: 'agentSession.cancel', fields: { turnId: 'turn-1' }, fence }), - turnId: 'turn-1' - } - case 'agentSession.respondToApproval': - case 'agentSession.respondToQuestion': { - const fields = { itemId: 'item-1', expectedRevision: 1, optionId: 'allow' } - return { envelope: envelope({ method, fields, fence }), ...fields } - } - case 'agentSession.requestHandoff': { - const fields = { - direction: 'to-tui' as const, - mode: 'now' as const, - action: 'start' as const - } - return { envelope: envelope({ method, fields, fence }), ...fields } - } - case 'agentSession.setOption': { - const fields = { key: 'model', value: 'gpt-5' } - return { envelope: envelope({ method, fields, fence }), ...fields } - } - case 'agentSession.history': - return { sessionId: SESSION, direction: 'tail' } - case 'agentSession.hold': - case 'agentSession.release': - return { sessionId: SESSION, holderId: 'surface-1' } - default: - return { sessionId: SESSION } - } -} - function runtimeStub(): unknown { const cleanups = new Map void>() return { @@ -407,9 +205,9 @@ describe('cross-version structured agent sessions', () => { let hostCalls: Record> beforeEach(() => { - operations = 0 + resetOperationIds() hostCalls = structuredHostStub(SESSION, WORKSPACE) - setStructuredAgentSessionHost(hostCalls as unknown as StructuredAgentSessionHost) + setStructuredAgentSessionHost(installableHost(hostCalls)) }) afterEach(() => { @@ -535,7 +333,7 @@ describe('cross-version structured agent sessions', () => { // `structured_agent_session_unsupported`, the same words the capability // gate uses, and the run would read as a refusal rather than a miss. const hostCalls = structuredHostStub(SESSION, WORKSPACE) - await releasedCurrent.installStructuredHost(hostCalls) + await releasedCurrent.installStructuredHost(installableHost(hostCalls)) try { await expectDeclaredSurfaceExecutes( releasedCurrent, @@ -888,7 +686,7 @@ describe('cross-version structured agent sessions', () => { } beforeEach(async () => { - operations = 0 + resetOperationIds() root = await mkdtemp(join(tmpdir(), 'orca-cross-version-agent-session-')) runtime = runtimeStub() await bootHost('a') diff --git a/tests/e2e/cross-version-wire/structured-agent-session-host-fixture.ts b/tests/e2e/cross-version-wire/structured-agent-session-host-fixture.ts index bb92409b078..6cf13ecff24 100644 --- a/tests/e2e/cross-version-wire/structured-agent-session-host-fixture.ts +++ b/tests/e2e/cross-version-wire/structured-agent-session-host-fixture.ts @@ -14,6 +14,14 @@ export function structuredHostStub( workspaceId: string ): Record> { return { + // The restart-resume surface hangs off a host MEMBER rather than the root, but its spies stay + // flat here: callers iterate this map asserting every entry is a spy that did not run, and + // `installableHost` below is what reassembles the member. Keeping them flat also lets the + // manifest name them to prove a call reached the host. + restartResumableList: vi.fn(async () => []), + restartResumableDismiss: vi.fn(async () => 0), + restartResumeAll: vi.fn(async () => []), + restartContinueAll: vi.fn(async () => ({ resumed: [], continued: [] })), attach: vi.fn(async () => ({ ok: true, replayed: false, value: { sessionId } })), // Attach-shaped entries take a client-supplied location, so the host is asked whether it // supports creating there. A real host always answers; leaving it unstubbed made every @@ -74,6 +82,26 @@ export function structuredHostStub( } } +/** The stub shaped the way the host actually exposes it: flat spies, plus the `restartResume` + * member the RPC methods reach through. Install this; assert against the flat map. + * + * The one assertion lives here so no call site needs its own. */ +export function installableHost( + hostCalls: Record> +): StructuredAgentSessionHost { + const host = { + ...hostCalls, + restartResume: { + list: hostCalls.restartResumableList, + dismiss: hostCalls.restartResumableDismiss, + resume: hostCalls.restartResumeAll, + continueAfterRestart: hostCalls.restartContinueAll + } + } + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: a spy map standing in for the host; the dispatcher reaches only the members stubbed above, and a missing one fails the call rather than type-checking. + return host as unknown as StructuredAgentSessionHost +} + const TURN = { turnId: 'turn-1', state: 'completed' as const, startedAt: 1, completedAt: 6 } const TURN_ROW = { itemId: 'legacy:codex:s:turn-1', revision: 1, sequence: 1, observedAt: 1 } @@ -86,7 +114,7 @@ export const turnItemSkew = { const host = structuredHostStub(sessionId, workspaceId) const items = [{ ...TURN_ROW, body: { kind: 'turn', ...TURN } }] host.history.mockReturnValue({ ok: true, page: { items } }) - setStructuredAgentSessionHost(host as unknown as StructuredAgentSessionHost) + setStructuredAgentSessionHost(installableHost(host)) }, /** Each skew's advertised list and the item it must be published. */ clients( diff --git a/tests/e2e/cross-version-wire/structured-agent-session-surface-manifest.ts b/tests/e2e/cross-version-wire/structured-agent-session-surface-manifest.ts new file mode 100644 index 00000000000..0bb0b43f20f --- /dev/null +++ b/tests/e2e/cross-version-wire/structured-agent-session-surface-manifest.ts @@ -0,0 +1,267 @@ +// What the structured `agentSession.*` surface IS, and how to call each method. +// +// Split from the skew scenarios so the manifest stays readable as it grows: this file answers +// "which methods exist, what each must reach and return, and what params it takes"; the suite +// next to it answers "what happens when the two builds disagree". +// +// Adding a method here is the deliberate act the cross-version gate exists to force. A new entry +// makes the suite call it in both skew directions, so an addition cannot land without someone +// stating what an older peer does with it. + +import { attachFingerprintFields } from '../../../src/main/native-chat/agent-session-wire/structured-agent-session-attach' +import type { AgentSessionAttachParams } from '../../../src/main/native-chat/agent-session-wire/structured-agent-session-attach' +import { computeAgentSessionPayloadFingerprint } from '../../../src/shared/agent-session-mutation-envelope' + +export const SESSION = 'session-alpha' +export const WORKSPACE = 'workspace-1' +export const THREAD = '019fd532-7c11-7a90-b6de-4e1a2c3d5f60' +export const NOW = 1_800_000_000_000 +export const REWIND_METHOD = 'agentSession.rewind' +export const STATUS_FEED_METHOD = 'agentSession.subscribeStatus' + +let operations = 0 + +/** Each test starts the ledger's operation ids from zero, so one test's envelopes cannot be + * mistaken for a replay of another's. */ +export function resetOperationIds(): void { + operations = 0 +} + +/** `<13-digit ms>-<32 hex>`, the only shape the durable ledger accepts. */ +function operationId(): string { + operations += 1 + return `${NOW}-${operations.toString(16).padStart(32, '0')}` +} + +/** Every method the structured surface publishes: the host method it must reach, + * and the result it must hand back. A gate that hides one method and leaks + * another is the bug; so is a method that is registered and answers with an + * error, which is why `result` is declared per method rather than inferred from + * "did not say method_not_found". `result` is omitted only where the method + * legitimately answers with no reply at all. */ +export const STRUCTURED_CALLS: { + method: string + hostMethod: string | null + result?: Record +}[] = [ + { method: 'agentSession.createSupport', hostMethod: null, result: { supported: true } }, + { + method: 'agentSession.create', + hostMethod: 'attach', + result: { ok: true, replayed: false, value: { sessionId: SESSION } } + }, + { + method: 'agentSession.ensure', + hostMethod: 'attach', + result: { ok: true, replayed: false, value: { sessionId: SESSION } } + }, + { + method: 'agentSession.conversationCommand', + hostMethod: 'conversationCommand', + result: { ok: true, value: { command: 'compact', state: 'completed' } } + }, + { method: 'agentSession.send', hostMethod: 'send', result: { ok: true, replayed: false } }, + { method: 'agentSession.cancel', hostMethod: 'cancel', result: { ok: true, replayed: false } }, + { + method: REWIND_METHOD, + hostMethod: 'rewind', + result: { ok: true, replayed: false, value: { itemId: 'item-1', epoch: 'rewound-epoch' } } + }, + { method: 'agentSession.close', hostMethod: 'close', result: { ok: true } }, + { + method: 'agentSession.respondToApproval', + hostMethod: 'respondToPrompt', + result: { ok: true, replayed: false } + }, + { + method: 'agentSession.respondToQuestion', + hostMethod: 'respondToPrompt', + result: { ok: true, replayed: false } + }, + { + method: 'agentSession.setOption', + hostMethod: 'setOption', + result: { ok: true, replayed: false } + }, + { + method: 'agentSession.requestHandoff', + hostMethod: 'requestHandoff', + result: { status: { owner: 'native' } } + }, + { + method: 'agentSession.handoffStatus', + hostMethod: 'handoffStatus', + result: { owner: 'native' } + }, + { + method: 'agentSession.options', + hostMethod: 'readOptions', + result: { current: { model: 'gpt-live' } } + }, + { + method: 'agentSession.commands', + hostMethod: 'readCommands', + result: { commands: [{ name: 'clear', kind: 'command' }] } + }, + { + method: 'agentSession.reveal', + hostMethod: 'revealSession', + result: { ok: true, sessionId: SESSION, workspaceId: WORKSPACE, agent: 'codex', readable: true } + }, + { method: 'agentSession.hold', hostMethod: 'hold', result: { held: true } }, + // The restart-resume surface. Bare additions, not capability-negotiated: an RPC method's + // absence is explicit (`method_not_found`), which the old-dispatcher case below asserts, so a + // newer client learns it during negotiation instead of by being met with silence. + { + method: 'agentSession.restartResumable', + hostMethod: 'restartResumableList', + result: { sessions: [] } + }, + { + method: 'agentSession.restartResumableDismiss', + hostMethod: 'restartResumableDismiss', + result: { dismissed: 0 } + }, + { + method: 'agentSession.restartResume', + hostMethod: 'restartResumeAll', + result: { results: [] } + }, + { + method: 'agentSession.restartContinue', + hostMethod: 'restartContinueAll', + result: { resumed: [], continued: [] } + }, + { method: 'agentSession.release', hostMethod: 'release', result: { released: true } }, + { + method: 'agentSession.history', + hostMethod: 'history', + result: { ok: true, page: { items: [] } } + }, + // A subscription that opens with nothing to say answers with no reply at all, + // so reaching the host is the only signal that the gate opened. + { method: 'agentSession.subscribe', hostMethod: 'subscribe' }, + // The status feed opens with a snapshot of every session, so its first reply is the contract. + { + method: STATUS_FEED_METHOD, + hostMethod: 'subscribeStatus', + result: { type: 'snapshot', sessions: [] } + }, + // Teardown runs through the runtime's subscription registry rather than the + // host, so its reply is the only signal that the gate opened. + { method: 'agentSession.unsubscribe', hostMethod: null, result: { unsubscribed: true } } +] + +export function envelope(args: { + method: string + fields: Record + fence: number | null +}): Record { + return { + sessionId: SESSION, + clientOperationId: operationId(), + expectedRuntimeFence: args.fence, + payloadFingerprint: computeAgentSessionPayloadFingerprint({ + method: args.method, + sessionId: SESSION, + fields: args.fields + }) + } +} + +export function attachParams(fence: number | null): Record { + const params = { + envelope: { sessionId: SESSION, clientOperationId: operationId(), expectedRuntimeFence: fence }, + location: { + executionHostId: 'local', + wslDistro: null, + workspaceId: WORKSPACE, + workspaceKind: 'git-worktree' + }, + provider: 'codex', + agent: 'codex', + accountHome: { variable: 'CODEX_HOME', path: '/home/dev/.codex' }, + runtimeKind: 'native', + providerHandle: { kind: 'codex', threadId: THREAD } + } + return { + ...params, + envelope: { + ...params.envelope, + payloadFingerprint: computeAgentSessionPayloadFingerprint({ + method: 'agentSession.attach', + sessionId: SESSION, + fields: attachFingerprintFields(params as unknown as AgentSessionAttachParams) + }) + } + } +} + +export function createIntentParams(): Record { + const worktree = `id:${WORKSPACE}` + const fields = { worktree, agent: 'codex' } + return { envelope: envelope({ method: 'agentSession.create', fields, fence: null }), ...fields } +} + +export function sendParams(text: string, fence: number): Record { + const body = { kind: 'message', role: 'user', blocks: [{ type: 'text', text }] } + return { envelope: envelope({ method: 'agentSession.send', fields: { body }, fence }), body } +} + +/** Schema-valid params per method; values only need to survive validation. */ +export function paramsFor(method: string): unknown { + const fence = 1 + switch (method) { + case 'agentSession.createSupport': + return { worktree: `id:${WORKSPACE}`, agent: 'codex' } + case 'agentSession.create': + return createIntentParams() + case 'agentSession.ensure': + return attachParams(fence) + case 'agentSession.conversationCommand': { + const fields = { command: 'compact' } + return { envelope: envelope({ method, fields, fence }), ...fields } + } + case 'agentSession.send': + return sendParams('hi', fence) + case REWIND_METHOD: { + const fields = { itemId: 'item-1', expectedEpoch: 'current-epoch' } + return { envelope: envelope({ method, fields, fence }), ...fields } + } + case 'agentSession.cancel': + return { + envelope: envelope({ method: 'agentSession.cancel', fields: { turnId: 'turn-1' }, fence }), + turnId: 'turn-1' + } + case 'agentSession.respondToApproval': + case 'agentSession.respondToQuestion': { + const fields = { itemId: 'item-1', expectedRevision: 1, optionId: 'allow' } + return { envelope: envelope({ method, fields, fence }), ...fields } + } + case 'agentSession.requestHandoff': { + const fields = { + direction: 'to-tui' as const, + mode: 'now' as const, + action: 'start' as const + } + return { envelope: envelope({ method, fields, fence }), ...fields } + } + case 'agentSession.setOption': { + const fields = { key: 'model', value: 'gpt-5' } + return { envelope: envelope({ method, fields, fence }), ...fields } + } + case 'agentSession.history': + return { sessionId: SESSION, direction: 'tail' } + case 'agentSession.hold': + case 'agentSession.release': + return { sessionId: SESSION, holderId: 'surface-1' } + case 'agentSession.restartResumable': + case 'agentSession.restartResumableDismiss': + case 'agentSession.restartResume': + case 'agentSession.restartContinue': + // Whole-surface calls: they name no session, and resume/continue narrow by an optional list. + return {} + default: + return { sessionId: SESSION } + } +} From b66ef2e8a86d817773bec4d78d99b0a450ca30a9 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 17 Sep 2026 13:03:24 -0700 Subject: [PATCH 018/168] fix(agent-launch): resolve a launch scope, not a git worktree record (#21193) * fix(agent-launch): resolve a launch scope, not a git worktree record `agent.launch` asked the runtime for a managed worktree record and then read exactly one field off it, `.id`. That record does not exist for every workspace a launch can run in, so the request refused launches the method could otherwise run: the floating workspace resolves to a scope with an id and a path but no worktree row, and `showManagedTerminalWorkspace` throws `selector_not_found` rather than hand back the id it had already resolved. A folder workspace survived that only because the resolver fabricates a worktree row for it. The scope is the answer that is real for all three kinds, so the launch asks for that instead. `showManagedTerminalWorkspace` is unchanged - callers that genuinely need the git record still get it, and still get the refusal. With floating now reaching the mode decision, the host must know which kind of workspace it resolved. The kind is derived from the id it resolved itself, never accepted from a caller, and the route module's existing `floating` blocker does the rest: a workspace with nowhere to keep a session runs a terminal agent. Behaviour change, deliberate: a floating-workspace `agent.launch` used to fail with `selector_not_found` and now succeeds as a terminal agent. That is what lets the floating titlebar agent button move onto the shared launch command instead of driving tab startup itself. No wire change: `AgentLaunchTarget` is untouched. * test(agent-launch): cover floating RPC workspace resolution --- .../agent-launch-executor.test.ts | 48 +++++++++++ .../agent-launch/agent-launch-executor.ts | 14 ++++ src/main/agent-launch/agent-launch-mode.ts | 13 ++- .../orca-runtime-list-managed-worktrees.ts | 17 ++++ .../agent-launch-floating-workspace.test.ts | 79 +++++++++++++++++++ .../rpc/methods/agent-launch-replay.test.ts | 10 +-- .../rpc/methods/agent-launch.test-fixture.ts | 9 +++ .../runtime/rpc/methods/agent-launch.test.ts | 4 +- src/main/runtime/rpc/methods/agent-launch.ts | 13 ++- .../src/lib/agent-launch-route-input.ts | 10 +-- src/renderer/src/lib/agent-launch-routing.ts | 3 +- .../structured-native-chat-launch-route.ts | 4 +- src/shared/workspace-launch-kind.ts | 23 ++++++ 13 files changed, 224 insertions(+), 23 deletions(-) create mode 100644 src/main/runtime/rpc/methods/agent-launch-floating-workspace.test.ts create mode 100644 src/shared/workspace-launch-kind.ts diff --git a/src/main/agent-launch/agent-launch-executor.test.ts b/src/main/agent-launch/agent-launch-executor.test.ts index 8910ccd1e60..7d7de2b2b59 100644 --- a/src/main/agent-launch/agent-launch-executor.test.ts +++ b/src/main/agent-launch/agent-launch-executor.test.ts @@ -15,6 +15,7 @@ import { type AgentLaunchExecution } from './agent-launch-executor' import type { AgentLaunchIntent } from '../../shared/agent-launch-intent' +import { FLOATING_TERMINAL_WORKTREE_ID } from '../../shared/constants' const STRUCTURED_PREFERENCE = { experimentalNativeChat: true, @@ -247,3 +248,50 @@ describe('the prompt receipt', () => { expect((await h.run(CREATE_INTENT)).prompt).toBeUndefined() }) }) + +/** + * The kind is read off the resolved workspace id, so a workspace with nowhere to keep a session is + * decided here rather than offered to a host probe that cannot answer for it. + */ +describe('a launch into an existing workspace, by workspace kind', () => { + it('runs the floating workspace as a terminal, never a structured session', async () => { + const h = harness({}) + const result = await h.run({ + agent: 'claude', + target: { kind: 'existing', worktree: FLOATING_TERMINAL_WORKTREE_ID } + }) + + // The invariant, not the call order: the floating sentinel has no session store to open into. + expect(h.createStructuredSession).not.toHaveBeenCalled() + expect(result.outcome).toEqual({ kind: 'terminal', handle: 'term_1' }) + expect(result.receipt).toMatchObject({ + mode: 'terminal', + reason: 'structured_unsupported_on_host' + }) + }) + + it('still opens a structured session in a folder workspace', async () => { + const h = harness({}) + const result = await h.run({ + agent: 'claude', + target: { kind: 'existing', worktree: 'folder:fw-1' } + }) + + // A folder workspace has no git worktree either; it must not be swept up with the sentinel. + expect(h.createTerminalAgent).not.toHaveBeenCalled() + expect(result.outcome).toEqual({ + kind: 'structured', + sessionId: 'sess-1', + handle: 'handle_structured' + }) + expect(result.receipt).toMatchObject({ mode: 'structured' }) + }) + + it('still opens a structured session in a git worktree', async () => { + const h = harness({}) + const result = await h.run({ agent: 'claude', target: { kind: 'existing', worktree: 'wt-7' } }) + + expect(result.outcome.kind).toBe('structured') + expect(result.receipt).toMatchObject({ mode: 'structured' }) + }) +}) diff --git a/src/main/agent-launch/agent-launch-executor.ts b/src/main/agent-launch/agent-launch-executor.ts index c65f63061fa..a92874b3505 100644 --- a/src/main/agent-launch/agent-launch-executor.ts +++ b/src/main/agent-launch/agent-launch-executor.ts @@ -32,6 +32,10 @@ import type { } from '../../shared/agent-launch-intent' import { withoutReservedAgentCreateFields } from '../../shared/agent-launch-intent' import type { TuiAgent } from '../../shared/tui-agent' +import { + workspaceKindForWorktreeId, + type WorkspaceLaunchKind +} from '../../shared/workspace-launch-kind' import type { OrcaRuntimeService } from '../runtime/orca-runtime' import { isDefinitiveAgentSessionCreateRefusal } from '../../shared/agent-session-definitive-refusal' import { @@ -107,6 +111,7 @@ export async function executeAgentLaunch( const preflight = decideAgentLaunchMode({ placement: { agent: intent.agent, + workspaceKind: launchWorkspaceKind(intent.target), ...(intent.reuseTerminal ? { terminal: intent.reuseTerminal.handle } : {}) }, settings, @@ -279,6 +284,15 @@ function existingWorktreeId(target: AgentLaunchTarget): string { return target.kind === 'existing' ? target.worktree : '' } +/** + * Read from the id rather than carried alongside it, so the kind cannot disagree with the workspace + * it describes. `worktree` here is never a caller's selector — the method resolved it to an id + * before building the intent — and a create always produces a git worktree. + */ +function launchWorkspaceKind(target: AgentLaunchTarget): WorkspaceLaunchKind { + return target.kind === 'existing' ? workspaceKindForWorktreeId(target.worktree) : 'git-worktree' +} + /** Prompt delivery is the caller's, not the executor's: a PTY paste is observed by whoever owns * the pane, and a structured first turn is sent through the session. The executor reports the * requested delivery back as not delivered so a caller cannot mistake silence for delivery. */ diff --git a/src/main/agent-launch/agent-launch-mode.ts b/src/main/agent-launch/agent-launch-mode.ts index 7533c0f2a01..1aee0e65cc3 100644 --- a/src/main/agent-launch/agent-launch-mode.ts +++ b/src/main/agent-launch/agent-launch-mode.ts @@ -32,6 +32,7 @@ import { } from '../../shared/structured-native-chat-launch-route' import type { TuiAgent } from '../../shared/tui-agent' import { hasExplicitTuiLaunchCommand } from '../../shared/tui-agent-launch-command-override' +import type { WorkspaceLaunchKind } from '../../shared/workspace-launch-kind' import type { OrcaRuntimeService } from '../runtime/orca-runtime' // The receipt is part of the launch contract, so it is declared with the rest of it; re-exported @@ -67,6 +68,10 @@ export type AgentLaunchModePlacement = { on?: string /** An existing terminal being reused. */ terminal?: string + /** Which kind of workspace the launch lands in, derived by the host from the workspace it + * resolved — never accepted from a caller, which would let one route around this decision. + * Absent means the kind was never established, and is not read as any particular kind. */ + workspaceKind?: WorkspaceLaunchKind } const DOWNGRADE_DETAIL: Record, string> = { @@ -131,9 +136,11 @@ export function decideAgentLaunchMode(args: { executionHostId: placement.on ? `runtime:${placement.on}` : 'local', reusesTerminal: Boolean(placement.terminal), hostCapabilities: RUNTIME_CAPABILITIES, - // A resolved managed worktree or folder workspace is never a floating terminal. WSL is left to - // the executing host's own create-support probe, which reads the resolved workspace rather - // than guessing from a client-side project runtime. + // The floating workspace has nowhere to keep a session, so it is decided here rather than left + // to the host probe below, which cannot answer for a workspace with no record. WSL still is: + // the create-support probe reads the resolved workspace rather than guessing from a + // client-side project runtime. + ...(placement.workspaceKind ? { workspaceKind: placement.workspaceKind } : {}), requiresTuiLaunchCommand: hasExplicitTuiLaunchCommand(settings, agent) }) if (!support.supported) { diff --git a/src/main/runtime/orca-runtime-list-managed-worktrees.ts b/src/main/runtime/orca-runtime-list-managed-worktrees.ts index 69aa37b6ef0..7fe067e9276 100644 --- a/src/main/runtime/orca-runtime-list-managed-worktrees.ts +++ b/src/main/runtime/orca-runtime-list-managed-worktrees.ts @@ -8,6 +8,7 @@ import { stopMissingWorktreeTerminals } from './missing-worktree-terminal-reconc import type { RuntimeCommandSurfaceHost } from './orca-runtime-core' import type { WorktreeVisibilitySourceMatcher } from '../../shared/worktree/visibility-sources' import type { RuntimeStore } from './runtime-store-contract' +import type { TerminalWorkspaceLaunchScope } from './runtime-legacy-worker-terminal-recovery-types' import type { WorkspacePortKillRequest, WorkspacePortKillResult, @@ -118,6 +119,10 @@ export class OrcaRuntimeWithListManagedWorktrees extends OrcaRuntimeWithRestoreS return await this.resolveWorktreeSelector(worktreeSelector) } + /** + * The git worktree record behind a terminal workspace. Refuses the floating sentinel, which has + * no such record — callers that only need to address the workspace want the scope below instead. + */ async showManagedTerminalWorkspace(worktreeSelector: string) { const target = await this.resolveTerminalWorkspaceLaunchTarget(worktreeSelector) if (!target.managedWorktree) { @@ -126,6 +131,18 @@ export class OrcaRuntimeWithListManagedWorktrees extends OrcaRuntimeWithRestoreS return target.managedWorktree } + /** + * Where a terminal workspace is, for every kind one can be: a git worktree, a folder workspace, + * or the floating sentinel. This is the general answer — `id` and `path` are resolved the same + * way for all three — so a caller that reads only those must ask for this rather than demand a + * worktree record it never reads and lose the floating workspace to a `selector_not_found`. + */ + async showTerminalWorkspaceLaunchScope( + worktreeSelector: string + ): Promise { + return await this.resolveTerminalWorkspaceLaunchScope(worktreeSelector) + } + async scanWorkspacePorts(repoId?: string): Promise { return scanWorkspacePortProbes(await this.getWorkspacePortProbes(repoId)) } diff --git a/src/main/runtime/rpc/methods/agent-launch-floating-workspace.test.ts b/src/main/runtime/rpc/methods/agent-launch-floating-workspace.test.ts new file mode 100644 index 00000000000..5551c4de354 --- /dev/null +++ b/src/main/runtime/rpc/methods/agent-launch-floating-workspace.test.ts @@ -0,0 +1,79 @@ +import { homedir } from 'node:os' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants' +import { OrcaRuntimeService } from '../../orca-runtime' +import { AGENT_LAUNCH_METHODS } from './agent-launch' +import { CAPABLE_CLIENT, methodNamed, STRUCTURED_PREFERENCE } from './agent-launch.test-fixture' + +vi.mock('electron', () => ({ + BrowserWindow: { fromId: vi.fn(() => null) }, + webContents: { fromId: vi.fn(() => null) }, + ipcMain: { on: vi.fn(), removeListener: vi.fn() }, + app: { getPath: vi.fn(() => '/tmp') } +})) + +const launch = methodNamed(AGENT_LAUNCH_METHODS, 'agent.launch') +const selectors = [FLOATING_TERMINAL_WORKTREE_ID, `id:${FLOATING_TERMINAL_WORKTREE_ID}`] + +afterEach(() => vi.restoreAllMocks()) + +describe('agent.launch with the real floating workspace resolver', () => { + it.each(selectors)('resolves %s without a managed worktree record', async (selector) => { + const runtime = new OrcaRuntimeService() + + await expect(runtime.showManagedTerminalWorkspace(selector)).rejects.toThrow( + 'selector_not_found' + ) + await expect(runtime.showTerminalWorkspaceLaunchScope(selector)).resolves.toEqual({ + id: FLOATING_TERMINAL_WORKTREE_ID, + path: homedir(), + connectionId: null, + repo: null, + folderWorkspace: null + }) + }) + + describe.each([true, false])('structured preference %s', (structuredPreference) => { + it.each(selectors)('launches a terminal through %s', async (selector) => { + const runtime = new OrcaRuntimeService() + vi.spyOn(runtime, 'getClientSettings').mockReturnValue( + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the launch reads only these preferences and optional agentCmdOverrides; no other settings consumer runs because terminal creation is stubbed. + { + ...STRUCTURED_PREFERENCE, + openAgentTabsInChatByDefault: structuredPreference + } as ReturnType + ) + const scope = vi.spyOn(runtime, 'showTerminalWorkspaceLaunchScope') + const createSupport = vi.spyOn(runtime, 'getStructuredAgentSessionCreateSupport') + const structuredHost = vi.spyOn(runtime, 'ensureStructuredAgentSessionHost') + const createTerminal = vi.spyOn(runtime, 'createTerminal').mockResolvedValue({ + handle: 'term_floating', + tabId: 'tab_floating', + worktreeId: FLOATING_TERMINAL_WORKTREE_ID, + title: 'Claude', + surface: 'background' + }) + + const result = await launch.handler( + launch.params.parse({ agent: 'claude', target: { kind: 'existing', worktree: selector } }), + { runtime, ...CAPABLE_CLIENT } + ) + + expect(scope).toHaveBeenCalledExactlyOnceWith(selector) + expect(createSupport).not.toHaveBeenCalled() + expect(structuredHost).not.toHaveBeenCalled() + expect(createTerminal).toHaveBeenCalledExactlyOnceWith( + `id:${FLOATING_TERMINAL_WORKTREE_ID}`, + { startupAgent: 'claude' } + ) + expect(result).toMatchObject({ + worktreeId: FLOATING_TERMINAL_WORKTREE_ID, + outcome: { kind: 'terminal', handle: 'term_floating' }, + receipt: { + mode: 'terminal', + reason: structuredPreference ? 'structured_unsupported_on_host' : 'user_default' + } + }) + }) + }) +}) diff --git a/src/main/runtime/rpc/methods/agent-launch-replay.test.ts b/src/main/runtime/rpc/methods/agent-launch-replay.test.ts index 6787bcfcf10..3d6981ed4a1 100644 --- a/src/main/runtime/rpc/methods/agent-launch-replay.test.ts +++ b/src/main/runtime/rpc/methods/agent-launch-replay.test.ts @@ -339,7 +339,7 @@ describe('an uncertain launch stays uncertain', () => { it('records a failure that happened before anything could be created', async () => { const runtime = runtimeStub() - runtime.showManagedTerminalWorkspace.mockRejectedValueOnce(new Error('worktree_not_found')) + runtime.showTerminalWorkspaceLaunchScope.mockRejectedValueOnce(new Error('worktree_not_found')) await expect( launch( @@ -481,7 +481,7 @@ describe('an unreadable launch payload costs one replay, never the store', () => describe('a recorded failure replays as the failure it was', () => { it('answers with the code the launch actually raised, not the ledger vocabulary', async () => { const runtime = runtimeStub() - runtime.showManagedTerminalWorkspace.mockRejectedValue(new Error('worktree_not_found')) + runtime.showTerminalWorkspaceLaunchScope.mockRejectedValue(new Error('worktree_not_found')) const params = createLaunch({ operationId: OPERATION_ID, target: { kind: 'existing', worktree: 'gone' } @@ -493,14 +493,14 @@ describe('a recorded failure replays as the failure it was', () => { // malformed" signal, which tells a client to mint a fresh id when the truthful answer is that // this launch definitively did not run. const replayed = runtimeStub() - replayed.showManagedTerminalWorkspace.mockRejectedValue(new Error('worktree_not_found')) + replayed.showTerminalWorkspaceLaunchScope.mockRejectedValue(new Error('worktree_not_found')) await expect(launch(params, replayed)).rejects.toThrow('worktree_not_found') - expect(replayed.showManagedTerminalWorkspace).not.toHaveBeenCalled() + expect(replayed.showTerminalWorkspaceLaunchScope).not.toHaveBeenCalled() }) it('bounds the code it persists, because a code is an identifier and a message is not', async () => { const runtime = runtimeStub() - runtime.showManagedTerminalWorkspace.mockRejectedValue( + runtime.showTerminalWorkspaceLaunchScope.mockRejectedValue( new Error(`ENOENT: no such file or directory, stat '${'/very/long/path'.repeat(400)}'`) ) diff --git a/src/main/runtime/rpc/methods/agent-launch.test-fixture.ts b/src/main/runtime/rpc/methods/agent-launch.test-fixture.ts index 7272b5bb7af..6b52635fc72 100644 --- a/src/main/runtime/rpc/methods/agent-launch.test-fixture.ts +++ b/src/main/runtime/rpc/methods/agent-launch.test-fixture.ts @@ -73,6 +73,15 @@ export function runtimeStub(options: AgentLaunchRuntimeStubOptions = {}) { showManagedTerminalWorkspace: vi.fn(async (selector: string) => ({ id: selector.replace(/^id:/, '') })), + // The scope resolves for every workspace kind, so unlike the worktree record above it never + // refuses the floating sentinel — which is the whole reason the launch asks for this one. + showTerminalWorkspaceLaunchScope: vi.fn(async (selector: string) => ({ + id: selector.replace(/^id:/, ''), + path: '/tmp/wt-7', + connectionId: null, + repo: null, + folderWorkspace: null + })), ensureStructuredAgentSessionHost: vi.fn(async () => {}), waitForSetupTerminalCompletion } diff --git a/src/main/runtime/rpc/methods/agent-launch.test.ts b/src/main/runtime/rpc/methods/agent-launch.test.ts index 6806726f0d2..2175aab0234 100644 --- a/src/main/runtime/rpc/methods/agent-launch.test.ts +++ b/src/main/runtime/rpc/methods/agent-launch.test.ts @@ -449,7 +449,9 @@ describe('the terminal factory', () => { ) expect(runtime.createManagedWorktree).not.toHaveBeenCalled() - expect(runtime.showManagedTerminalWorkspace).toHaveBeenCalledWith('id:wt-7') + // The scope, not the worktree record: asking for the record refused any workspace without one. + expect(runtime.showTerminalWorkspaceLaunchScope).toHaveBeenCalledWith('id:wt-7') + expect(runtime.showManagedTerminalWorkspace).not.toHaveBeenCalled() // Resolved to an id first: everything below re-prefixes it, so a raw selector reaches the // runtime as `id:id:wt-7`. expect(runtime.createTerminal).toHaveBeenCalledWith('id:wt-7', { startupAgent: 'grok' }) diff --git a/src/main/runtime/rpc/methods/agent-launch.ts b/src/main/runtime/rpc/methods/agent-launch.ts index af6e018a36d..787652fa293 100644 --- a/src/main/runtime/rpc/methods/agent-launch.ts +++ b/src/main/runtime/rpc/methods/agent-launch.ts @@ -59,17 +59,22 @@ export function supportsAgentLaunch( /** * A client addresses a workspace by selector, but the result's `worktreeId` is an id and every * step below the executor re-prefixes it as `id:`. Resolving here is what keeps a - * caller's `id:wt-7` from reaching the runtime as `id:id:wt-7`; the terminal-workspace resolver is - * used rather than the git-worktree one so a folder workspace is addressable too. + * caller's `id:wt-7` from reaching the runtime as `id:id:wt-7`. + * + * The launch *scope* is what is asked for, because the id below is the only thing read off it. The + * git-worktree record is the narrower answer — it does not exist for the floating workspace, so + * asking for one refused a launch this method can perfectly well run, on a workspace whose id it + * had already resolved. A folder workspace survived that only because the resolver fabricates a + * worktree row for it; the scope is the answer that is real for all three kinds. */ async function agentLaunchTarget( params: AgentLaunchParams, - runtime: Pick + runtime: Pick ): Promise { if (params.target.kind === 'create-worktree') { return { kind: 'create-worktree', create: { ...params.target.create } } } - const workspace = await runtime.showManagedTerminalWorkspace(params.target.worktree) + const workspace = await runtime.showTerminalWorkspaceLaunchScope(params.target.worktree) return { kind: 'existing', worktree: workspace.id } } diff --git a/src/renderer/src/lib/agent-launch-route-input.ts b/src/renderer/src/lib/agent-launch-route-input.ts index 85fbf2e8ca3..2f0c1e083f4 100644 --- a/src/renderer/src/lib/agent-launch-route-input.ts +++ b/src/renderer/src/lib/agent-launch-route-input.ts @@ -1,11 +1,10 @@ -import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants' import { LOCAL_EXECUTION_HOST_ID, parseExecutionHostId, toRuntimeExecutionHostId } from '../../../shared/execution-host' import type { TuiAgent } from '../../../shared/tui-agent' -import { parseWorkspaceKey } from '../../../shared/workspace-scope' +import { workspaceKindForWorktreeId } from '../../../shared/workspace-launch-kind' import { hasExplicitTuiLaunchCommand, type AgentLaunchRoutingInput @@ -57,12 +56,7 @@ export type AgentLaunchRouteArgs = { initialSessionOptions?: Readonly> } -export function workspaceKindForWorktreeId(worktreeId: string): ProspectiveWorkspaceKind { - if (worktreeId === FLOATING_TERMINAL_WORKTREE_ID) { - return 'floating' - } - return parseWorkspaceKey(worktreeId)?.type === 'folder' ? 'folder' : 'git-worktree' -} +export { workspaceKindForWorktreeId } function resolveExecutionHostId(store: AgentLaunchRouteStore, workspace: ProspectiveWorkspace) { if (workspace.worktreeId) { diff --git a/src/renderer/src/lib/agent-launch-routing.ts b/src/renderer/src/lib/agent-launch-routing.ts index ba614053131..67d47cf1d23 100644 --- a/src/renderer/src/lib/agent-launch-routing.ts +++ b/src/renderer/src/lib/agent-launch-routing.ts @@ -5,6 +5,7 @@ import { resolveStructuredNativeChatSupport } from '../../../shared/structured-native-chat-launch-route' import type { TuiAgent } from '../../../shared/tui-agent' +import type { WorkspaceLaunchKind } from '../../../shared/workspace-launch-kind' import { decideInitialAgentTabViewMode, type NativeChatLaunchPromptDelivery @@ -28,7 +29,7 @@ export type AgentLaunchRoutingInput = { executionHostId: string /** Capabilities of the target host; `null` = not yet established. */ hostCapabilities: readonly string[] | null - workspaceKind?: 'git-worktree' | 'folder' | 'floating' + workspaceKind?: WorkspaceLaunchKind projectRuntime?: ProjectExecutionRuntimeResolution | null promptDelivery?: NativeChatLaunchPromptDelivery launchText?: string diff --git a/src/shared/structured-native-chat-launch-route.ts b/src/shared/structured-native-chat-launch-route.ts index bde8de06c0a..5ae855ea47e 100644 --- a/src/shared/structured-native-chat-launch-route.ts +++ b/src/shared/structured-native-chat-launch-route.ts @@ -13,6 +13,7 @@ import type { GlobalSettings } from './global-settings-types' import type { ProjectExecutionRuntimeResolution } from './project-execution-runtime' import { STRUCTURED_AGENT_SESSION_RUNTIME_CAPABILITY } from './protocol-version' import type { TuiAgent } from './tui-agent' +import type { WorkspaceLaunchKind } from './workspace-launch-kind' export type NativeChatDefaultSettings = Pick< GlobalSettings, @@ -44,7 +45,8 @@ export type StructuredNativeChatSupportInput = { executionHostId: string /** Capabilities of the host this launch would run on. `null` = not yet established. */ hostCapabilities: readonly string[] | null - workspaceKind?: 'git-worktree' | 'folder' | 'floating' + /** Host-derived. Absent means the kind was never established, which is not evidence of any kind. */ + workspaceKind?: WorkspaceLaunchKind projectRuntime?: ProjectExecutionRuntimeResolution | null requiresTuiLaunchCommand?: boolean /** An existing PTY agent keeps its execution transport. */ diff --git a/src/shared/workspace-launch-kind.ts b/src/shared/workspace-launch-kind.ts new file mode 100644 index 00000000000..b4c03ef96dc --- /dev/null +++ b/src/shared/workspace-launch-kind.ts @@ -0,0 +1,23 @@ +/** + * Which kind of workspace a launch lands in, read from the workspace's own id. + * + * The three kinds are not interchangeable to a launch: only a git worktree and a folder workspace + * have somewhere a structured session can live, and the floating terminal — a sentinel with no + * backing repo, worktree or folder row — can host a PTY and nothing else. + * + * It lives in `shared` because both sides of the launch ask the same question: the renderer when a + * user opens an agent tab, and the host when it resolves an `agent.launch` target. A host must + * never take the answer from a caller, so it derives it here from the id it resolved itself. + */ + +import { FLOATING_TERMINAL_WORKTREE_ID } from './constants' +import { parseWorkspaceKey } from './workspace-scope' + +export type WorkspaceLaunchKind = 'git-worktree' | 'folder' | 'floating' + +export function workspaceKindForWorktreeId(worktreeId: string): WorkspaceLaunchKind { + if (worktreeId === FLOATING_TERMINAL_WORKTREE_ID) { + return 'floating' + } + return parseWorkspaceKey(worktreeId)?.type === 'folder' ? 'folder' : 'git-worktree' +} From 9de6f2c6cdacf3e3dcc59946bc8d1542315f3e9e Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 16:28:11 -0400 Subject: [PATCH 019/168] test(terminal): bump the pane hook-order parity pin past #9035 (#21276) * test(terminal): bump the pane hook-order parity pin past #9035 #9035 added a useRef and a useCallback to use-terminal-pane-foundation (search input ref, focus-search-input) without moving the parity pin, and its own PR run never executed the shard that holds it. Every PR opened since fails `tests node 24 7/8` on `expected 211 to have a length of 209`. The two hooks are in order behind the existing ones and useMemo stays at 8. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(terminal): re-pin the hook-order hash for the two #9035 hooks The count alone was not the pin: the flattened order is hashed too. The new order is the old one with useRef and useCallback inserted at the foundation stage and nothing else moved (diffed before and after #9035). Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- .../terminal-pane/terminal-pane-hook-order-parity.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/components/terminal-pane/terminal-pane-hook-order-parity.test.ts b/src/renderer/src/components/terminal-pane/terminal-pane-hook-order-parity.test.ts index 2eb22ecfb03..d53306b031e 100644 --- a/src/renderer/src/components/terminal-pane/terminal-pane-hook-order-parity.test.ts +++ b/src/renderer/src/components/terminal-pane/terminal-pane-hook-order-parity.test.ts @@ -18,8 +18,10 @@ const TERMINAL_PANE_HOOK_SOURCE_PATTERN = // paused notice that read it (207 hooks, still 8 useMemo). // Then host-authoritative layout removal added two `useRef`s in reconciliation // (last host layout leaf set, retired leaf set) (209 hooks, still 8 useMemo). +// Then search match count + Cmd+F focus parity (#9035) added a `useRef` and a `useCallback` in +// foundation (search input ref, focus-search-input) (211 hooks, still 8 useMemo). const PRE_REFACTOR_HOOK_ORDER_SHA256 = - 'f6de13ab7d6d130444c50fec2cfe097851ee1b7ecf0f3a2cbdc082c2e8e8838b' + 'ed41829b57f0723c155af1b0339513f5191e4485cd2e7b3fa2c062039afdc4e3' const sourceFiles = readdirSync(__dirname) .filter((name) => TERMINAL_PANE_HOOK_SOURCE_PATTERN.test(name)) @@ -84,7 +86,7 @@ function readFlattenedHookOrder(): string[] { describe('TerminalPane refactor hook parity', () => { it('preserves the recursively flattened render hook order', () => { const hooks = readFlattenedHookOrder() - expect(hooks).toHaveLength(209) + expect(hooks).toHaveLength(211) expect(hooks.filter((hook) => hook === 'useMemo')).toHaveLength(8) expect(createHash('sha256').update(hooks.join('\n')).digest('hex')).toBe( PRE_REFACTOR_HOOK_ORDER_SHA256 From 4a86b2dc565dea05491573afdbff17d6217806d5 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 16:38:16 -0400 Subject: [PATCH 020/168] refactor(mobile): checked reply readers for files, dictation, host-screen and agent-history (step 7) (#21269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test(mobile): record main's file-preview and markdown-disk-fallback replies Four of this branch's read sites had no malformed-reply coverage, so the reader change would have had nothing to move at them. `familyGoldens` matrixes only the first scenario of each family, and `files.preview-load`'s base is the grant-refresh chain while `session.tab-documents`' is the served markdown tab — which left `files.read` and `files.readPreview` on the worktree preview path, the artifact image read, and the markdown tab's on-disk fallback recorded on their success path only. This commit is the before picture, taken from main's own tree with no product edit in it. Three new families, five scenarios, ten goldens: - `files.preview-worktree-text` / `files.preview-worktree-image` — `files.read` and `files.readPreview` as the preview screen asks them for a worktree file. - `files.preview-artifact-image` — `files.readTerminalArtifactPreview`. - `session.markdown-disk-fallback` — the `files.read` leg a headless host's `renderer_unavailable` sends the markdown tab down. It carries a second scenario that serves `markdown.readTab`, because a matrix site needs a fulfilled reply recorded somewhere in its own family to replay as the `normal` partition. No existing scenario moved to a new family and no adapter changed, so every pre-existing golden keeps its `adapterSha256` and `scenarioSha256`. Recorded in a detached worktree at the manifest's pin (`4b876758d3`) with this manifest copied in; the control is that all 748 pre-existing goldens came back byte-identical to origin/main's, which `git diff c2962a765a -- mobile/rpc-foundation/goldens` confirms as empty. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): checked reply readers for files, dictation, host-screen and agent-history Thirty-five unchecked reply readers across seven files become checked zod readers, so a malformed host reply surfaces as one readable error at the operation boundary instead of a downstream TypeError, a rendered `undefined`, or a screen left ready over garbage. Deliberately a behaviour change on malformed replies only: every good reply decodes to the same value it did, which the `normal` partition of every matrix golden holds byte for byte. Nothing on the wire moves — no method, params, options, timeout or acceptance policy changes at any site. The inventory drops from 137 readers over 31 files to 102 over 24. What each domain checks, and what it deliberately does not: - files/preview — one schema for `files.read` and `files.readTerminalArtifact`, one for the two preview methods. `content` is required on the text pair because the markdown disk fallback publishes it into the tab with no guard; the image pair requires nothing, because normalizeImagePreviewResult guards all four members and the host's own "binary I cannot preview" and "not actually an image" arms are good replies the screen renders today. - files/tab-doc — stricter than the preview screen on the same two methods, because a tab publishes what it read into a typed ready document with no guard. `git.diff` reads as two variants, and an arm this build has not heard of takes the binary one rather than refusing the reply. - files/explorer — the directory listing is an array and a row needs the name and the directory flag the tree projection turns on; the legacy capped list needs its rows' paths and the truncation flag its note draws. - files/ownership — the two members that decide *where a write lands* are fatal on a wrong type rather than salvaged, because absence reads as `local` downstream and a salvage would send a mutation to the wrong host. `hostId`'s absent/null/string states stay distinct, and the SSH connection generation passes through at its own type because the mutation echoes it back to the host. - dictation — the setup the sheet renders is checked; the model rows need the `id` the sheet keys and sends back. The five sends whose reply body no call site reads keep an unknown payload, and so does `speech.dictation.finish`, whose transcript is read past a staleness guard that a reader throw would move the failure across. - host-screen — the repo catalog, the SSH labels and the host platform. The four writes read no reply body; `worktree.activate` stays opaque because the session route's second report site awaits it outside any catch. - agent-history — the capability gate and both scan containers. The session rows stay unknown on purpose: `agent` is a vocabulary that grows with every agent CLI Orca learns to scan and that this client echoes back on resume, so narrowing it would refuse a newer host's reply or drop the very sessions it added. Two shared readers were widened to take the strings the reply readers hand them — `getRepoExecutionHostId` and `buildRepoHostIdByRepoId` — because both already answer `local` for a host-id spelling they cannot parse, and closing that spelling in a reply schema would refuse a newer host's own catalog. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): repin the corpus and re-record the checked reply readers `baseline` moves to this branch's last fenced commit, which is what `--record` refuses without: main's fenced tree drifted past the session domain's pin when #21114 and the dependency bump landed, and the product edit in the commit before this one moves it again. Every body move is confined to a malformed partition of a family this branch touched. No `normal` partition moved, which is the byte-for-byte control on good replies, and no golden outside the seven files' families moved at all. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): stop the dictation reader requiring a mode main rendered without The setup sheet's `normal` partition refused after the reader landed, which is the success control saying the schema was wrong rather than the fixture: `dictationMode` was declared required because the one unguarded consumer pushes it into a `useState<'toggle' | 'hold'>` and cannot invent a value, but main rendered a sheet whose reply omitted it, and requiring a member no consumer crashes on is exactly the version claim Rule 1 of the remote-wire contract warns about. The member is salvaged now and keeps its open arm set, so an unknown mode still degrades to `toggle` rather than to one that matches no segment. The native-chat refresh spells that same `toggle` for an absent mode, which is the value its state already started at, and the route parity pins are refreshed for the one literal and the two callback bodies that moved. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): repin past the dictation fix and re-record Second repin of the branch: the fix to the setup reader is a fenced-tree change, so `--record` refuses until `baseline` names it. The speech family's `normal` partition is back to main's projection, which is what said the first reader was wrong. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): mutant evidence for the checked reply readers Three mutations applied by hand, run, and reverted, recorded beside the adapter family mutations in the same shape. They are kept in their own file because a reader mutation is not killed by a pilot scenario: a pilot serves a good reply, and a schema that has stopped checking a member reads a good reply exactly as before. What kills them is a matrix golden's malformed partition, the schema's unit pin, or a consumer pin, and each is named against its mutation. Two survived their first run, and both survivals were defects in the gates: - Loosening the file tab's `content` was invisible, because the pin dropped members only in pairs and each pair is refused by the sibling. The pin now drops exactly one member per iteration, and the preview text schema and the legacy file list got the same treatment. - Collapsing the hostId tri-state was invisible, because no golden serves an explicit null host — the local ownership scenario omits the member. The ownership test now captures all three states end to end, which is where a tri-state belongs. `repo-metadata-platform` is re-anchored where this branch moved the read it mutates: the hand-rolled `readHostPlatform` became the reply schema's own projection. The defect it injects is unchanged. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): record main's repo-icon and speech-vocabulary replies The closed enums this branch introduced had no fixture behind them. `provider`, `dictationMode` and `repoIcon` were carried by no scenario at all — the fulfilled repo-metadata golden records `repoIconsByName: []` — so the corpus could not have moved whatever arm set the schemas declared, which is how a reader can pin a vocabulary the host does not speak and still decode to a zero-move delta. Two scenarios, both appended to an existing family so `familyGoldens` adds no matrix golden, recorded from main's own tree at the pin with no product edit in it: - `settings-repo-metadata-icons` — all three `RepoIcon` arms, a github-sourced image with a label, an explicit `badgeColor`, and a mixed-host catalog so the ssh/settings/platform wave runs too. - `speech-setup-sheet-model-vocabulary` — `provider` on both arms, `status` on two, `dictationMode: "hold"`, and null and numeric `sizeBytes`/`progress`. Control: re-recording the whole corpus at the pin reproduces every committed golden body, including this branch's five earlier before-pictures; only `baseline` and the masked `lockfileSha256` move. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): stop the repo icon narrowing a member no consumer reads The image arm of `repoIcon` declared `source` as the four values `RepoIconImageSource` spells today (src/shared/repo-icon.ts:3). MobileRepoIcon reads `type`, `src`, `label`, `emoji` and `name`, and never `source`, so the only thing that enum could do was fail the union arm for a source a later host adds — dropping the whole icon and drawing the Folder default where main drew the image. That is the one arm set on this branch whose degrade was not already main's own behaviour for an unknown value. Dropping the declaration keeps the member: `looseObject` passes it through verbatim, so the decoded object is byte-identical to the one main published, which `settings-repo-metadata-icons` now records. The two type sites that hold an icon move to the decoded type. A host `RepoIcon` still satisfies the rendered union, so the worktree rows that carry one are unaffected. Every other closed enum on this branch was checked against the host's own shared type and left alone: speech `provider`/`status`/`dictationMode` (runtime-worktree-contracts.ts:83/85/86), `groupBy`/`sortBy` (persisted-ui-state-types.ts:41-42), `platform` (Node's own domain; the handler answers `process.platform`). For each, a salvaged member lands on the same branch main's unknown value did: `=== 'openai'` and `=== 'ready'` stay false, a missing `groupBy` and an unmapped one both answer null, and an unknown platform and a null one both label the host "This computer". Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): repin past the repo-icon fix and re-record Header-only: all 770 goldens move on `baseline` alone, including the two recorded from main's tree two commits back. The icon fix and the two new fixtures decode to the bytes main published. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): keep the repo-metadata readers total the way main's were readSshTargets and readHostPlatform answered [] and null for any payload at all. The checked schemas threw for a non-object, and because the label write runs first in the same sequence that throw also skipped the platform write, so a malformed reply left both decorative labels at their previous values instead of degrading. A .catch on each restores main's answer without giving up the row filter or the checked reader. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): forward the dictation mode instead of substituting a default The reader closed the mode to two arms and the native-chat refresh spelled `?? 'toggle'`, which is a good-reply change no golden covers: main left the state undefined for a reply that omits the mode, and undefined binds no press handler on the terminal input mic. Head gave that mic a working toggle. The member is forwarded as the string the host sent and the refresh is main's line again, so an absent or unknown mode leaves the mic exactly as inert as main's. The route-parity runtime-string pin is main's own sha again. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): repin past the review fixes and re-record The repo-metadata readers are total again, so both families' `result-absent` and `result-null` checkpoints decode to main's bytes instead of the caught throw, and the two delta rows they cost go away. The dictation mode forwards verbatim, which no recorded reply exercises differently. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): repin onto the merge and re-record Pins the corpus to the merge commit so main's ten create-terminal goldens and this branch's own are recorded from one tree. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): correct three reader comments round 2 caught The ownership schema said an explicit null hostId means the host said local; the code refuses it, which is the whole reason mutant (c) exists. The AiVault sessions cast cited a golden whose fixture row carries three members, not the sixteen the cast claims — the full row is in aivault-history-screen-listed — and both the issues cast and the schema doc said the rows are rendered when the only read anywhere is issues.length. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): correct stale file:line citations in the batch-A reply schemas Resolved every citation in the seven reply-schema modules and the SAFETY notes against the tree and diffed each target line against the claim beside it. Twelve were wrong, two of them past the end of a file that had shrunk, so they read as evidence while pointing at a closing brace. - file-explorer: the entries put is :157 not :160, the relativePath split is file-list-fallback.ts:48 not :42, and the truncated publish is :136 not :141. buildFileExplorerRows is no symbol at all; the sort-and-walk is flattenDirectoryCache (file-tree.ts:58). - file-ownership: the !summary throw is :68 not :64. - file-preview: the markdown disk fallback reads content at :60 not :65. - file-tab-doc: the html body render is :68 not :81 and the file arm is :73-75 not :86-88 (the file has 78 lines); the isImage guard is :58 not :66; the kind !== 'text' branch is :41 not :44; mobileDiffImageDataUri spans :22-33 not :20-31; the unguarded content.length is mobile-diff-lines.ts:35, the function that does it rather than :34. - agent-history: both members land at :133-135; :135 alone is issues. - dictation: the parenthetical read as citing the staleness guard when it named the rpcPayloadMember read. Both are cited now, :237 and :225. Comments only. No schema, type, or runtime behaviour changes. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): name the unguarded activation report site that pins the opaque schema Handler audit over all 33 interpret sites in the four domains found one site that is structurally unguarded: use-mobile-session-startup.ts:170 reports the activation verdict from inside a fire-and-forget `void (async …)()` whose only `.catch` sits on the request, not on the chain. A throw there would be an unhandled rejection and would also skip the terminal fetch below it. Nothing throws there today, because `worktree.activate` reads hostScreenUnreadReplySchema, which is `z.unknown()`. That totality is load bearing rather than incidental, so the doc now names the line it protects and contrasts it with the first report site at :141, which is chained `.then(…).catch(…)` and would survive a throw. Comments only. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): pin that a bound descriptor's interpret survives being detached bindDeferredRpcOperation builds interpret as a shorthand method closing over the captured operation, never `this`, which is what lets eleven call sites pass it as a bare function reference. Nothing named that invariant. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * chore(mobile): repin the RPC recording baseline to the main merge Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): pin the closed reply enums to the host unions where tsc looks pullfrog: the PR body promised a Record pin for every closed enum in this batch and the code had none. Adding them in the schema tests would have changed nothing: mobile/tsconfig.json excludes *.test.ts, so a coverage record there is never typechecked (a mutation that dropped a key stayed green). hostUnionArms(coverage) in zod-salvage spells the arm list as a Readonly> in the schema module itself, called with the host union as the explicit type argument: an arm the host adds is a missing property, one it drops is an excess property. Used for the speech provider and status (RuntimeSpeechModelSummary), the workspace groupBy and sortBy (PersistedUIState) and Node's platform list, which host-screen now imports from mobile-runtime-host-platform instead of duplicating. The repo icon branches satisfy Readonly>. Three mutations (drop `manual`, add `bogus`, drop the image branch) each fail tsc. The tests iterate the exported lists; the platform mutant is re-anchored to the renamed constant. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- .../aivault-history-scan-fulfilled.json | 2 +- .../aivault-history-scan-unsupported.json | 2 +- .../aivault-history-scan-worktrees-late.json | 2 +- .../aivault-history-screen-listed.json | 2 +- .../aivault-history-screen-worktrees.json | 2 +- .../aivault-resume-launch-create-refused.json | 2 +- .../aivault-resume-launch-invalid-tab.json | 2 +- .../goldens/aivault-resume-launch-locked.json | 2 +- .../goldens/aivault-resume-launch-sent.json | 2 +- .../aivault-resume-prepare-refused.json | 2 +- .../goldens/aivault-resume-prepare-repin.json | 2 +- .../aivault-resume-prepare-skipped.json | 2 +- .../aivault-resume-prepare-unavailable.json | 2 +- mobile/rpc-foundation/goldens/b1.json | 2 +- mobile/rpc-foundation/goldens/b2.json | 2 +- mobile/rpc-foundation/goldens/b3.json | 2 +- .../goldens/browser-dialog-accepted.json | 2 +- .../goldens/browser-dialog-dismissed.json | 2 +- .../goldens/browser-keyboard-input.json | 2 +- .../browser-pointer-click-accepted.json | 2 +- .../browser-pointer-click-fallback.json | 2 +- .../goldens/browser-wheel-scrolled.json | 2 +- .../clipboard-image-attachment-anonymous.json | 2 +- ...-image-attachment-blocked-before-send.json | 2 +- .../clipboard-image-attachment-cancelled.json | 2 +- .../clipboard-image-attachment-pasted.json | 2 +- ...board-image-attachment-upload-refused.json | 2 +- ...-image-upload-aborts-on-chunk-failure.json | 2 +- .../clipboard-image-upload-chunked.json | 2 +- ...rd-image-upload-single-frame-fallback.json | 2 +- .../clipboard-image-upload-start-refused.json | 2 +- .../goldens/codex-reset-credit-consumed.json | 2 +- .../goldens/codex-reset-credit-resumed.json | 2 +- .../goldens/components-codex-capability.json | 2 +- .../goldens/components-setup-ask.json | 2 +- .../goldens/components-target-local.json | 2 +- .../goldens/components-target-ssh.json | 2 +- .../goldens/diff-review-branch-compare.json | 2 +- .../goldens/diff-review-branch-file-diff.json | 2 +- ...f-review-notes-refused-before-compare.json | 2 +- .../diff-review-refused-file-diff.json | 2 +- .../goldens/diff-review-snapshot.json | 2 +- .../diff-review-status-unavailable.json | 2 +- .../diff-review-worktree-file-diff.json | 2 +- .../goldens/file-tap-open-refused.json | 2 +- .../goldens/file-tap-opens-worktree-file.json | 2 +- .../file-tap-previews-absolute-artifact.json | 2 +- .../goldens/file-tap-resolve-miss.json | 2 +- .../goldens/file-tap-resolve-refused.json | 2 +- .../files-explorer-legacy-fallback.json | 2 +- .../goldens/files-explorer-readdir.json | 2 +- .../goldens/files-ownership-local.json | 2 +- .../goldens/files-ownership-ssh.json | 2 +- .../files-preview-artifact-direct.json | 2 +- .../files-preview-artifact-image-read.json | 95 +++ .../goldens/files-preview-artifact-image.json | 2 +- .../goldens/files-preview-grant-refresh.json | 2 +- .../files-preview-worktree-image-read.json | 94 +++ .../goldens/files-preview-worktree-image.json | 2 +- .../files-preview-worktree-text-read.json | 97 +++ .../goldens/files-preview-worktree.json | 2 +- .../goldens/files-save-blind.json | 2 +- .../goldens/files-save-verified.json | 2 +- .../goldens/files-tab-doc-shapes.json | 2 +- .../goldens/home-host-accounts.json | 2 +- .../goldens/home-host-stats.json | 2 +- .../goldens/host-view-settings-sync.json | 2 +- ...host-worktree-actions-pin-open-delete.json | 2 +- .../goldens/host-worktree-delete-refused.json | 2 +- .../goldens/host-worktree-refresh-stream.json | 2 +- .../interruptions-inventory-lifecycle.json | 2 +- ...ions-settings-bot-overrides-fulfilled.json | 2 +- .../goldens/inventory-lifecycle.json | 2 +- .../goldens/inventory-repeat-query.json | 2 +- .../rpc-foundation/goldens/lifecycle-b3.json | 2 +- .../lifecycle-inventory-lifecycle.json | 2 +- ...ycle-settings-bot-overrides-fulfilled.json | 2 +- ...cle-settings-task-hydration-fulfilled.json | 2 +- ...-settings-workspace-context-fulfilled.json | 2 +- .../goldens/linear-select-workspace.json | 2 +- .../goldens/live-worktree-name-stream.json | 2 +- ...ructured-create-agentsession.create-1.json | 2 +- ...d-create-agentsession.createsupport-1.json | 2 +- ...d-launch-agentsession.createsupport-1.json | 2 +- ...ivault.history-aivault.listsessions-1.json | 49 +- ...ivault.history-screen-platform-status.json | 2 +- ...x-aivault.history-screen-status.get-2.json | 69 +- ...-aivault.history-screen-worktree.ps-1.json | 2 +- .../matrix-aivault.history-status.get-1.json | 42 +- ...-launch-session.tabs.createterminal-1.json | 2 +- ...aivault.resume-launch-terminal.send-1.json | 2 +- ...ration-aivault.preparesessionresume-1.json | 2 +- ...browser.dialog-browser.dialogaccept-1.json | 2 +- ...keyboard-browser.keyboardinserttext-1.json | 2 +- ...x-browser.keyboard-browser.keypress-1.json | 2 +- ...er.pointer-click-browser.mouseclick-1.json | 2 +- ...ser.pointer-click-browser.mousedown-1.json | 2 +- ...ser.pointer-click-browser.mousemove-1.json | 2 +- ...owser.pointer-click-browser.mouseup-1.json | 2 +- ...rix-browser.wheel-browser.mousemove-1.json | 2 +- ...ix-browser.wheel-browser.mousewheel-1.json | 2 +- ...tachment-clipboard.startimageupload-1.json | 2 +- ...pload-clipboard.saveimageastempfile-1.json | 2 +- ...e-upload-clipboard.startimageupload-1.json | 2 +- ...s.codex-reset-capability-status.get-1.json | 2 +- ...it-accounts.consumecodexresetcredit-1.json | 2 +- ...target-local-preflight.detectagents-1.json | 2 +- ...target-preflight.detectremoteagents-1.json | 2 +- ...onents.execution-target-ssh.connect-1.json | 2 +- ...nents.execution-target-ssh.getstate-1.json | 2 +- ...ew-workspace-repositories-repo.list-1.json | 2 +- ...-components.setup-script-repo.hooks-1.json | 2 +- ...ix-files.explorer-screen-files.list-1.json | 82 +-- ...files.explorer-screen-files.readdir-1.json | 52 +- ...les.mutation-ownership-ssh.getstate-1.json | 37 +- ...files.mutation-ownership-status.get-1.json | 2 +- ...es.mutation-ownership-worktree.show-1.json | 37 +- ...e-files.readterminalartifactpreview-1.json | 643 ++++++++++++++++++ ...iew-load-files.readterminalartifact-1.json | 33 +- ...iew-load-files.readterminalartifact-2.json | 33 +- ...view-load-files.resolveterminalpath-1.json | 21 +- ...iew-save-files.readterminalartifact-1.json | 33 +- ...ew-save-files.writeterminalartifact-1.json | 2 +- ...ew-worktree-image-files.readpreview-1.json | 632 +++++++++++++++++ ...es.preview-worktree-text-files.read-1.json | 618 +++++++++++++++++ .../matrix-files.tab-doc-files.read-1.json | 104 +-- ...rix-files.tab-doc-files.readpreview-1.json | 53 +- .../matrix-files.tab-doc-git.diff-1.json | 53 +- ...-files.terminal-path-tap-files.open-1.json | 2 +- ...-path-tap-files.resolveterminalpath-1.json | 2 +- ....base-ref-chain-repo.baserefdefault-1.json | 2 +- ...matrix-git.base-ref-chain-repo.list-1.json | 2 +- ...ix-git.base-ref-chain-worktree.show-1.json | 2 +- ....branch-diff-preview-git.branchdiff-1.json | 2 +- ...-git.changes-load-git.branchcompare-1.json | 2 +- .../matrix-git.changes-load-git.status-1.json | 2 +- .../matrix-git.changes-load-repo.list-1.json | 2 +- ...trix-git.changes-load-worktree.show-1.json | 2 +- ...essage-ai-git.generatecommitmessage-1.json | 2 +- ...tory-commit-files-git.commitcompare-1.json | 2 +- ...it.history-commit-files-git.history-1.json | 2 +- ...matrix-git.history-read-git.history-1.json | 2 +- ...ix-git.remote-prerequisite-git.push-1.json | 2 +- ...x-git.review-preparation-git.status-1.json | 2 +- ...ent-mutation-github.addissuecomment-1.json | 2 +- ...tion-github.addprreviewcommentreply-1.json | 2 +- ...ub.project.deleteissuecommentbyslug-1.json | 2 +- ...ub.project.updateissuecommentbyslug-1.json | 2 +- ...mutation-github.resolvereviewthread-1.json | 2 +- ...x-github.pr-mutation-github.mergepr-1.json | 2 +- ...r-mutation-github.removeprreviewers-1.json | 2 +- ...-mutation-github.requestprreviewers-1.json | 2 +- ...ub.pr-mutation-github.rerunprchecks-1.json | 2 +- ...b.pr-mutation-github.setprautomerge-1.json | 2 +- ...ub.pr-mutation-github.updateprstate-1.json | 2 +- ....pr-read-github.listassignableusers-1.json | 2 +- ...ithub.pr-read-github.prcheckdetails-1.json | 2 +- ...trix-github.pr-read-github.prchecks-1.json | 2 +- ...x-github.pr-read-github.prforbranch-1.json | 2 +- ...trix-github.pr-read-github.reposlug-1.json | 2 +- ...thub.pr-read-github.workitemdetails-1.json | 2 +- ...thub.pr-read-hostedreview.forbranch-1.json | 2 +- ...title-mutation-github.updateprtitle-1.json | 2 +- ...ix-home.host-accounts-accounts.list-1.json | 2 +- ...atrix-home.host-stats-stats.summary-1.json | 2 +- ...sh-runtime.clientevents.subscribe-1-1.json | 2 +- ...sh-runtime.clientevents.subscribe-1-2.json | 2 +- ...sh-runtime.clientevents.subscribe-1-3.json | 2 +- ...sh-runtime.clientevents.subscribe-2-1.json | 2 +- .../matrix-host.view-settings-ui.get-1.json | 2 +- .../matrix-host.view-settings-ui.set-1.json | 2 +- ....worktree-actions-worktree.activate-1.json | 2 +- ...x-host.worktree-actions-worktree.rm-1.json | 2 +- ...-host.worktree-actions-worktree.set-1.json | 2 +- ...-hostedreview.create-chain-git.push-1.json | 2 +- ...ew.create-chain-hostedreview.create-1.json | 2 +- ...tedreview.create-chain-worktree.set-1.json | 2 +- ...dreview.create-intent-git.bulkstage-1.json | 2 +- ...stedreview.create-intent-git.commit-1.json | 2 +- ...te-intent-git.generatecommitmessage-1.json | 2 +- ...hostedreview.create-intent-git.push-1.json | 2 +- ...stedreview.create-intent-git.status-1.json | 2 +- ...stedreview.create-intent-git.status-2.json | 2 +- ...stedreview.create-intent-git.status-3.json | 2 +- ...stedreview.create-intent-git.status-4.json | 2 +- ...w.create-intent-hostedreview.create-1.json | 2 +- ...hostedreview.getcreationeligibility-1.json | 2 +- ...hostedreview.getcreationeligibility-2.json | 2 +- ...edreview.create-intent-worktree.set-1.json | 2 +- ...hostedreview.getcreationeligibility-1.json | 2 +- ...-legacy-inventory-files.searchpaths-1.json | 2 +- ...-legacy-inventory-files.searchpaths-2.json | 2 +- ...trix-legacy-inventory-fresh-inventory.json | 2 +- ...matrix-legacy-inventory-old-inventory.json | 2 +- ...near-detail-barrier-linear.getissue-1.json | 2 +- ...detail-barrier-linear.issuecomments-1.json | 2 +- ...space-picker-linear.selectworkspace-1.json | 2 +- ...me-runtime.clientevents.subscribe-1-1.json | 2 +- ...me-runtime.clientevents.subscribe-1-2.json | 2 +- ...me-runtime.clientevents.subscribe-2-1.json | 2 +- ...ix-live-worktree-name-worktree.show-1.json | 2 +- ...ix-live-worktree-name-worktree.show-2.json | 2 +- ...ix-live-worktree-name-worktree.show-3.json | 2 +- ...ativechat.image-paste-terminal.send-1.json | 2 +- ...ativechat.image-paste-terminal.send-2.json | 2 +- ...e-upload-clipboard.startimageupload-1.json | 2 +- ...ings.mutatenativechatsessionoptions-1.json | 2 +- ...chestration.workerterminaluserinput-1.json | 2 +- ...vechat.terminal-write-terminal.send-1.json | 2 +- ...stream-notifications.getmissedsince-1.json | 2 +- ...op-stream-notifications.subscribe-1-1.json | 2 +- ...op-stream-notifications.subscribe-1-2.json | 2 +- ...op-stream-notifications.unsubscribe-1.json | 2 +- ...-test-screen-notifications.testpush-1.json | 2 +- ...missal-notifications.getmissedsince-1.json | 2 +- ...stration-notifications.registerpush-1.json | 2 +- ...ration-notifications.unregisterpush-1.json | 2 +- ...rix-pairing.pre-profile-direct-status.json | 2 +- ...ng.pre-profile-pairing.getendpoints-1.json | 2 +- ....pre-profile-pairing.provisionrelay-1.json | 2 +- ...trix-pairing.pre-profile-relay-status.json | 2 +- ...se-github.project.updateissuebyslug-1.json | 2 +- ...ntial-rotation-pairing.getendpoints-1.json | 2 +- ...ntial-rotation-pairing.getendpoints-2.json | 2 +- ...ial-rotation-pairing.provisionrelay-1.json | 2 +- ...direct-upgrade-pairing.getendpoints-1.json | 2 +- ...direct-upgrade-pairing.getendpoints-2.json | 2 +- ...rect-upgrade-pairing.provisionrelay-1.json | 2 +- ...iring-recovery-pairing.getendpoints-1.json | 2 +- ...rowser-tab-create-browser.tabcreate-1.json | 2 +- ...ion.content-create-files.createfile-1.json | 2 +- ...x-session.content-create-files.open-1.json | 2 +- ...x-session.content-create-status.get-1.json | 2 +- ...ession.content-create-worktree.show-1.json | 51 +- ...erminal-session.tabs.createterminal-1.json | 2 +- ...ssion.create-terminal-terminal.send-1.json | 2 +- ...ix-session.diff-notes-worktree.show-1.json | 2 +- ...on.diff-review-actions-worktree.set-1.json | 2 +- ...rix-session.diff-review-base-ref-show.json | 2 +- ...ssion.diff-review-git.branchcompare-1.json | 2 +- ...trix-session.diff-review-git.status-1.json | 2 +- ...atrix-session.diff-review-repo.list-1.json | 2 +- ...atrix-session.diff-review-review-show.json | 2 +- ...n.markdown-disk-fallback-files.read-1.json | 619 +++++++++++++++++ ...down-disk-fallback-markdown.readtab-1.json | 581 ++++++++++++++++ ...sion.markdown-save-markdown.savetab-1.json | 2 +- ...ve-chat-page-nativechat.readsession-1.json | 2 +- ...ve-chat-page-nativechat.subscribe-1-1.json | 2 +- ...ve-chat-page-nativechat.subscribe-2-1.json | 2 +- ...n.native-chat-readability-repo.list-1.json | 2 +- ...chestration.workerterminaluserinput-1.json | 2 +- ...sion.native-chat-stop-terminal.send-1.json | 2 +- ...sion.native-chat-stop-terminal.send-2.json | 2 +- ...pr-branch-context-git.branchcompare-1.json | 2 +- ...ession.pr-branch-context-git.status-1.json | 2 +- ...session.pr-branch-context-repo.list-1.json | 2 +- ...ion.pr-branch-context-worktree.show-1.json | 2 +- ...-session.pr-sidebar-github.prchecks-1.json | 2 +- ...ssion.pr-sidebar-github.prforbranch-1.json | 2 +- ...n.pr-sidebar-hostedreview.forbranch-1.json | 2 +- ...ix-session.pr-sidebar-worktree.show-1.json | 2 +- ...-triage-session.tabs.createterminal-1.json | 2 +- ...rix-session.pr-triage-terminal.send-1.json | 2 +- ...n.review-branch-diff-git.branchdiff-1.json | 2 +- ...x-session.review-file-diff-git.diff-1.json | 2 +- ...x-session.review-file-diff-git.diff-2.json | 2 +- ...x-session.review-file-diff-git.diff-3.json | 2 +- ...on.review-git-mutations-git.discard-1.json | 2 +- ...sion.review-git-mutations-git.stage-1.json | 2 +- ...sion.review-git-mutations-git.stage-2.json | 2 +- ...review-send-sheet-session.tabs.list-1.json | 2 +- ...x-session.startup-worktree.activate-1.json | 2 +- ...x-session.startup-worktree.activate-2.json | 2 +- ...ab-activation-session.tabs.activate-1.json | 2 +- ...ssion.tab-activation-terminal.focus-1.json | 2 +- ...ab-close-session-session.tabs.close-1.json | 2 +- ...ix-session.tab-close-terminal.close-1.json | 2 +- ...sion.tab-documents-markdown.readtab-1.json | 2 +- ...-session.tab-rename-terminal.rename-1.json | 2 +- ...on.tab-reveal-session.tabs.activate-1.json | 2 +- ...ession.tab-reveal-session.tabs.list-1.json | 2 +- ...abs-stream-health-session.tabs.list-1.json | 2 +- ...isplay-mode-terminal.setdisplaymode-1.json | 2 +- ...chestration.workerterminaluserinput-1.json | 2 +- ...-gesture-input-terminal.clearbuffer-1.json | 2 +- ...erminal-gesture-input-terminal.send-1.json | 2 +- ...chestration.workerterminaluserinput-1.json | 2 +- ...n.terminal-input-send-terminal.send-1.json | 2 +- ...on.terminal-inventory-terminal.list-1.json | 2 +- ...chestration.workerterminaluserinput-1.json | 2 +- ...session.terminal-paste-settings.get-1.json | 2 +- ...ession.terminal-paste-terminal.send-1.json | 2 +- ...ssion.worktree-connection-repo.list-1.json | 2 +- ...on.worktree-connection-settings.get-1.json | 2 +- ...t-read-preflight.detectremoteagents-1.json | 2 +- ...atrix-settings-agent-read-repo.list-1.json | 2 +- ...ix-settings-agent-read-settings.get-1.json | 2 +- ...ettings-best-effort-settings.update-1.json | 2 +- ...settings.bot-overrides-settings.get-1.json | 2 +- ...ttings.home-providers-linear.status-1.json | 2 +- ...ings.home-providers-preflight.check-1.json | 2 +- ...ettings.home-providers-settings.get-1.json | 2 +- ...local-agents-preflight.detectagents-1.json | 2 +- ...ings.new-tab-local-agents-repo.list-1.json | 2 +- ...s.new-tab-local-agents-settings.get-1.json | 2 +- ...s-settings.getterminalquickcommands-1.json | 2 +- ...ettings.updateterminalquickcommands-1.json | 2 +- ...ettings.repo-metadata-host.platform-1.json | 2 +- ...ix-settings.repo-metadata-repo.list-1.json | 2 +- ...settings.repo-metadata-settings.get-1.json | 2 +- ...po-metadata-ssh.listtargetsummaries-1.json | 2 +- ...esume-metadata-folderworkspace.list-1.json | 17 +- ...s.resume-metadata-projectgroup.list-1.json | 17 +- ...-settings.resume-metadata-repo.list-1.json | 28 +- ...ttings.resume-metadata-settings.get-1.json | 2 +- ...ettings.resume-metadata-worktree.ps-1.json | 17 +- ...ttings.task-hydration-linear.status-1.json | 2 +- ...ings.task-hydration-preflight.check-1.json | 2 +- ...ettings.task-hydration-settings.get-1.json | 2 +- ...-settings.task-hydration-status.get-1.json | 2 +- ...trix-settings.task-hydration-ui.get-1.json | 2 +- ....task-workspace-create-settings.get-1.json | 2 +- ...sk-workspace-create-worktree.create-1.json | 2 +- ...ettings.task-workspace-settings.get-1.json | 2 +- ...ngs.workspace-context-linear.status-1.json | 2 +- ...s.workspace-context-preflight.check-1.json | 2 +- ...ings.workspace-context-settings.get-1.json | 2 +- ...x-settings.workspace-context-ui.get-1.json | 2 +- ...tings.workspace-submit-settings.get-1.json | 2 +- ...tation-chunk-speech.dictation.chunk-1.json | 2 +- ...ion-session-speech.dictation.finish-1.json | 2 +- ...tion-session-speech.dictation.start-1.json | 2 +- ...ation-start-speech.dictation.cancel-1.json | 2 +- ...tation-start-speech.dictation.start-1.json | 2 +- ....setup-sheet-speech.dictation.setup-1.json | 172 +---- ...ch.setup-sheet-speech.models.delete-1.json | 210 +----- ....setup-sheet-speech.models.download-1.json | 2 +- ...eech.setup-sheet-speech.models.list-1.json | 176 +---- ...cks-files-github.addprreviewcomment-1.json | 2 +- ...-checks-files-github.prfilecontents-1.json | 2 +- ...m-checks-files-github.rerunprchecks-1.json | 2 +- ...ks-files-github.resolvereviewthread-1.json | 2 +- ...checks-files-github.setprfileviewed-1.json | 2 +- ...mment-github-github.addissuecomment-1.json | 2 +- ...mment-gitlab-gitlab.addissuecomment-1.json | 2 +- ...mment-gitlab-mr-gitlab.addmrcomment-1.json | 2 +- ...etail-github-github.workitemdetails-1.json | 2 +- ...etail-gitlab-gitlab.workitemdetails-1.json | 2 +- ....item-detail-linear-linear.getissue-1.json | 2 +- ...-detail-linear-linear.issuecomments-1.json | 2 +- ...metadata-github.listassignableusers-1.json | 2 +- ...m-detail-metadata-github.listlabels-1.json | 2 +- ...ks.item-merge-gitlab-gitlab.mergemr-1.json | 2 +- ...tem-metadata-github-github.updatepr-1.json | 2 +- ...-metadata-gitlab-gitlab.updateissue-1.json | 2 +- ...-metadata-gitlab-mr-gitlab.updatemr-1.json | 2 +- ...-reply-merge-github.addissuecomment-1.json | 2 +- ...erge-github.addprreviewcommentreply-1.json | 2 +- ...sks.item-reply-merge-github.mergepr-1.json | 2 +- ...item-reply-merge-linear.updateissue-1.json | 2 +- ....item-review-github-github.prchecks-1.json | 2 +- ...ew-github-github.requestprreviewers-1.json | 2 +- ...em-status-gitlab-github.updateissue-1.json | 2 +- ...em-status-gitlab-gitlab.updateissue-1.json | 2 +- ...atus-gitlab-mr-gitlab.updatemrstate-1.json | 2 +- ...tasks.linear-connect-linear.connect-1.json | 2 +- ....linear-item-linear.addissuecomment-1.json | 2 +- ...asks.linear-item-linear.createissue-1.json | 2 +- ...x-tasks.linear-item-linear.getissue-1.json | 2 +- ...inear-team-context-linear.listteams-1.json | 2 +- ...near-team-context-linear.teamstates-1.json | 2 +- ...-tasks.paste-lookup-github.reposlug-1.json | 2 +- ...-tasks.paste-lookup-github.workitem-1.json | 2 +- ...e-lookup-github.workitembyownerrepo-1.json | 2 +- ....paste-lookup-gitlab.workitembypath-1.json | 2 +- ...-load-github.project.listaccessible-1.json | 2 +- ...board-load-github.project.listviews-1.json | 2 +- ...board-load-github.project.listviews-2.json | 2 +- ...oard-load-github.project.resolveref-1.json | 2 +- ...board-load-github.project.viewtable-1.json | 2 +- ....project-repo-slugs-github.reposlug-1.json | 2 +- ...ithub.project.addissuecommentbyslug-1.json | 2 +- ...ue-github.project.updateissuebyslug-1.json | 2 +- ...ub.project.updateissuecommentbyslug-1.json | 2 +- ...hub.project.updatepullrequestbyslug-1.json | 2 +- ...ithub.project.workitemdetailsbyslug-1.json | 2 +- ...ields-github.project.clearitemfield-1.json | 2 +- ...ithub.project.updateissuetypebyslug-1.json | 2 +- ...elds-github.project.updateitemfield-1.json | 2 +- ...les-merge-github.addprreviewcomment-1.json | 2 +- ...ject-row-files-merge-github.mergepr-1.json | 2 +- ...w-files-merge-github.prfilecontents-1.json | 2 +- ...-row-files-merge-github.updateissue-1.json | 2 +- ...ow-files-merge-github.updateprstate-1.json | 2 +- ...b.project.listassignableusersbyslug-1.json | 2 +- ...github.project.listissuetypesbyslug-1.json | 2 +- ...oad-github.project.listlabelsbyslug-1.json | 2 +- ...t-row-review-checks-github.prchecks-1.json | 2 +- ...ew-checks-github.requestprreviewers-1.json | 2 +- ...-review-checks-github.rerunprchecks-1.json | 2 +- ...eview-checks-github.setprfileviewed-1.json | 2 +- ...-row-threads-github.addissuecomment-1.json | 2 +- ...eads-github.addprreviewcommentreply-1.json | 2 +- ...ub.project.deleteissuecommentbyslug-1.json | 2 +- ...-threads-github.resolvereviewthread-1.json | 2 +- ...provider-load-github.countworkitems-1.json | 2 +- ....provider-load-github.listworkitems-1.json | 2 +- ...asks.provider-load-linear.listteams-1.json | 2 +- ...x-tasks.provider-load-linear.status-1.json | 2 +- ...tasks.provider-load-settings.update-1.json | 2 +- ...rix-tasks.route-repo-list-repo.list-1.json | 2 +- ...-source-search-github.listworkitems-1.json | 2 +- ...-source-search-gitlab.listworkitems-1.json | 2 +- ...art-source-search-linear.listissues-1.json | 2 +- ...t-source-search-linear.searchissues-1.json | 2 +- ...smart-source-search-repo.searchrefs-1.json | 2 +- ...sk-create-github-github.createissue-1.json | 2 +- ...asks.task-create-github-repo.update-1.json | 2 +- ...sk-create-gitlab-gitlab.createissue-1.json | 2 +- ...sk-create-linear-linear.createissue-1.json | 2 +- ...t-gitlab-items-gitlab.listworkitems-1.json | 2 +- ...task-list-gitlab-todos-gitlab.todos-1.json | 2 +- ....task-list-linear-linear.listissues-1.json | 2 +- ...ask-list-linear-linear.searchissues-1.json | 2 +- ...ks.workspace-source-repo.searchrefs-1.json | 2 +- ...workspace-source-repo.sparsepresets-1.json | 2 +- ...kspace-sparse-repo.savesparsepreset-1.json | 2 +- ...tasks.workspace-sparse-ssh.getstate-1.json | 2 +- ...ce-ssh-local-preflight.detectagents-1.json | 2 +- ...ce-ssh-preflight.detectremoteagents-1.json | 2 +- ...trix-tasks.workspace-ssh-repo.hooks-1.json | 2 +- ...rix-tasks.workspace-ssh-ssh.connect-1.json | 2 +- ...-terminal.query-reply-terminal.send-1.json | 2 +- ...chestration.workerterminaluserinput-1.json | 2 +- ...ix-terminal.raw-input-terminal.send-1.json | 2 +- ...chestration.workerterminaluserinput-1.json | 2 +- ...chestration.workerterminaluserinput-2.json | 2 +- ...wport-refit-terminal.updateviewport-1.json | 2 +- ...ansport.capability-probe-status.get-1.json | 2 +- ...nsport.host-status-gates-status.get-1.json | 2 +- ...-transport.pairing-race-direct-status.json | 2 +- ...x-transport.pairing-race-relay-status.json | 2 +- ...ee.agent-launch-create-agent.launch-1.json | 2 +- ...rktree.catalog-snapshot-worktree.ps-1.json | 2 +- ...rktree.create-retry-worktree.create-1.json | 2 +- ...x-worktree.home-catalog-worktree.ps-1.json | 2 +- ....hosted-base-worktree.resolvemrbase-1.json | 2 +- ....hosted-base-worktree.resolveprbase-1.json | 2 +- ...red-names-worktree.listretirednames-1.json | 2 +- ...x-worktree.review-link-worktree.set-1.json | 2 +- ...ree.runtime-capabilities-status.get-1.json | 2 +- ...ix-worktree.setup-hook-trust-ui.set-1.json | 2 +- .../native-chat-image-paste-single.json | 2 +- ...e-chat-image-paste-stops-on-rejection.json | 2 +- ...ative-chat-image-paste-trailing-image.json | 2 +- .../native-chat-image-paste-two-images.json | 2 +- .../native-chat-image-upload-cancelled.json | 2 +- ...native-chat-image-upload-second-fails.json | 2 +- .../native-chat-image-upload-single.json | 2 +- ...ative-chat-image-upload-start-refused.json | 2 +- .../goldens/native-chat-image-upload-two.json | 2 +- .../goldens/native-chat-page-earlier.json | 2 +- .../native-chat-readability-local-repo.json | 2 +- .../native-chat-readability-refused.json | 2 +- .../native-chat-readability-remote-repo.json | 2 +- ...native-chat-session-option-pick-empty.json | 2 +- ...tive-chat-session-option-pick-refused.json | 2 +- ...tive-chat-session-option-pick-written.json | 2 +- .../goldens/native-chat-stop-accepted.json | 2 +- .../native-chat-stop-both-rejected.json | 2 +- .../native-chat-stop-delivery-unknown.json | 2 +- .../goldens/native-chat-write-accepted.json | 2 +- .../goldens/native-chat-write-clear-line.json | 2 +- .../native-chat-write-delivery-unknown.json | 2 +- .../goldens/native-chat-write-rejected.json | 2 +- .../native-chat-write-typed-command.json | 2 +- .../goldens/new-tab-local-agents.json | 2 +- .../new-workspace-repositories-fulfilled.json | 2 +- .../notifications-desktop-stream-closed.json | 2 +- ...notifications-desktop-stream-replayed.json | 2 +- .../goldens/notifications-desktop-stream.json | 2 +- .../notifications-display-test-accepted.json | 2 +- ...fications-display-test-not-registered.json | 2 +- ...tifications-display-test-rate-limited.json | 2 +- ...fications-display-test-unknown-reason.json | 2 +- .../notifications-push-gateway-rejected.json | 2 +- .../notifications-push-registered.json | 2 +- ...re-profile-direct-wins-and-provisions.json | 2 +- ...ovision-unsupported-saves-direct-host.json | 2 +- .../pairing-pre-profile-times-out.json | 2 +- .../goldens/pr-branch-identity.json | 2 +- .../goldens/pr-branch-repo-context.json | 2 +- .../goldens/pr-comment-mutation.json | 2 +- .../pr-comment-resolve-unconfirmed.json | 2 +- .../goldens/pr-mutation-in-band-failure.json | 2 +- .../goldens/pr-mutation-status.json | 2 +- .../goldens/pr-read-fork-routing.json | 2 +- .../goldens/pr-read-surface.json | 2 +- .../goldens/pr-read-upstream-error.json | 2 +- .../goldens/pr-sidebar-checks-refused.json | 2 +- .../goldens/pr-sidebar-load.json | 2 +- .../goldens/pr-title-mutation.json | 2 +- .../goldens/pr-title-unconfirmed.json | 2 +- .../goldens/pr-triage-invalid-terminal.json | 2 +- .../goldens/pr-triage-launch.json | 2 +- .../goldens/pr-triage-send-locked.json | 2 +- .../goldens/probe-new-tab-both-refused.json | 2 +- .../probe-new-tab-null-sibling-refused.json | 2 +- ...probe-new-tab-refused-sibling-rejects.json | 2 +- ...probe-new-tab-rejects-sibling-refused.json | 2 +- .../push-dismissal-tray-reconciled.json | 2 +- .../goldens/quick-commands-load-refused.json | 2 +- .../quick-commands-loaded-and-saved.json | 2 +- ...uick-commands-save-refused-rolls-back.json | 2 +- .../goldens/relay-direct-upgrade-commits.json | 2 +- ...ect-upgrade-unsupported-host-declines.json | 2 +- ...ay-pairing-recovery-invite-authorizes.json | 2 +- ...lay-pairing-recovery-resume-committed.json | 2 +- .../relay-rotation-installs-and-commits.json | 2 +- ...ay-rotation-resumes-committed-pending.json | 2 +- .../goldens/review-branch-diff-shapes.json | 2 +- .../review-create-terminal-refused.json | 2 +- .../goldens/review-file-diff-shapes.json | 2 +- .../goldens/review-git-mutations-run.json | 2 +- .../review-mark-reviewed-persists.json | 2 +- .../review-mark-reviewed-rolls-back.json | 2 +- .../goldens/review-open-in-session.json | 2 +- .../review-send-notes-heals-stale-input.json | 2 +- .../review-send-sheet-lists-terminals.json | 2 +- .../goldens/review-stage-file.json | 2 +- .../goldens/review-stage-refused.json | 2 +- .../goldens/sc-base-ref-default.json | 2 +- .../goldens/sc-base-ref-repo-fallback.json | 2 +- .../goldens/sc-base-ref-unavailable.json | 2 +- .../goldens/sc-base-ref-worktree-hit.json | 2 +- .../goldens/sc-branch-diff-previewed.json | 2 +- .../goldens/sc-changes-loaded.json | 2 +- .../sc-commit-message-cancel-rejected.json | 2 +- .../goldens/sc-commit-message-canceled.json | 2 +- .../goldens/sc-commit-message-generated.json | 2 +- .../goldens/sc-create-existing-review.json | 2 +- ...reate-intent-stage-commit-push-create.json | 2 +- .../sc-create-intent-unlisted-provider.json | 2 +- .../sc-create-link-failure-is-non-fatal.json | 2 +- .../sc-create-pushes-then-creates.json | 2 +- .../sc-create-refused-empty-message.json | 2 +- .../sc-create-rejected-empty-message.json | 2 +- .../goldens/sc-eligibility-fetched.json | 2 +- .../goldens/sc-history-commit-files.json | 2 +- .../goldens/sc-history-loaded.json | 2 +- .../goldens/sc-pr-link-hosted-review.json | 2 +- .../goldens/sc-pr-link-read.json | 2 +- .../goldens/sc-pr-link-set.json | 2 +- .../sc-prefill-unavailable-on-refusal.json | 2 +- .../sc-prefill-unavailable-on-rejection.json | 2 +- .../sc-prerequisite-force-with-lease.json | 2 +- .../goldens/sc-prerequisite-publish.json | 2 +- .../goldens/sc-prerequisite-push.json | 2 +- .../goldens/sc-prerequisite-skipped.json | 2 +- .../goldens/sc-reveal-first-poll.json | 2 +- .../goldens/sc-reveal-timeout.json | 2 +- .../sc-review-commit-inner-failure.json | 2 +- ...c-review-commit-refused-empty-message.json | 2 +- .../goldens/sc-review-commit-rejected.json | 2 +- .../goldens/sc-review-commit.json | 2 +- .../sc-review-status-entries-not-array.json | 2 +- .../goldens/sc-review-status-normalized.json | 2 +- .../rpc-foundation/goldens/schedules-b3.json | 2 +- ...les-settings-home-providers-fulfilled.json | 2 +- .../schedules-settings-new-tab-ssh.json | 2 +- ...ules-settings-repo-metadata-fulfilled.json | 2 +- ...es-settings-resume-metadata-fulfilled.json | 2 +- ...les-settings-task-hydration-fulfilled.json | 2 +- ...-settings-workspace-context-fulfilled.json | 2 +- .../goldens/session-browser-tab-created.json | 2 +- .../session-create-browser-refused.json | 2 +- .../goldens/session-create-browser-tab.json | 2 +- ...ession-create-markdown-name-collision.json | 2 +- .../goldens/session-create-markdown-note.json | 2 +- ...nal-ignores-a-second-create-in-flight.json | 2 +- ...minal-launches-an-agent-quick-command.json | 2 +- .../session-create-terminal-refused.json | 2 +- ...ssion-create-terminal-replaces-active.json | 2 +- ...-create-terminal-runs-a-quick-command.json | 2 +- .../session-create-terminal-with-prompt.json | 2 +- ...on-create-terminal-without-active-tab.json | 2 +- ...ession-create-terminal-without-handle.json | 2 +- .../session-diff-notes-load-refused.json | 2 +- .../goldens/session-diff-notes-loaded.json | 2 +- .../goldens/session-file-tab-read.json | 2 +- .../goldens/session-markdown-disk-read.json | 140 ++++ .../goldens/session-markdown-disk-served.json | 102 +++ .../session-markdown-save-conflict.json | 2 +- .../goldens/session-markdown-saved.json | 2 +- .../session-markdown-tab-disk-fallback.json | 2 +- .../goldens/session-markdown-tab-read.json | 2 +- .../goldens/session-markdown-tab-refused.json | 2 +- ...session-startup-both-activation-sites.json | 2 +- ...artup-floating-route-skips-activation.json | 2 +- ...-keeps-terminals-visible-on-reconnect.json | 2 +- ...efused-tab-load-still-loads-terminals.json | 2 +- ...ion-tab-activation-focus-and-activate.json | 2 +- .../session-tab-activation-refused.json | 2 +- ...ession-tab-activation-transport-error.json | 2 +- .../session-tab-close-refused-keeps-tab.json | 2 +- .../session-tab-close-session-tab.json | 2 +- .../goldens/session-tab-close-terminal.json | 2 +- .../goldens/session-tab-closed.json | 2 +- .../goldens/session-tab-rename.json | 2 +- .../goldens/session-tab-renamed.json | 2 +- .../goldens/session-tabs-health-errored.json | 2 +- .../session-tabs-health-reconciled.json | 2 +- .../goldens/session-tabs-health-refused.json | 2 +- ...abs-health-stale-application-revision.json | 2 +- ...terminal-display-mode-auto-take-floor.json | 2 +- ...isplay-mode-auto-without-device-token.json | 2 +- ...al-display-mode-auto-without-viewport.json | 2 +- ...inal-display-mode-drops-second-toggle.json | 2 +- ...sion-terminal-display-mode-to-desktop.json | 2 +- ...session-terminal-list-dedupes-handles.json | 2 +- .../session-terminal-list-empty-guarded.json | 2 +- .../goldens/session-terminal-list-merged.json | 2 +- .../session-terminal-list-refused.json | 2 +- .../settings-bot-overrides-fulfilled.json | 2 +- ...ettings-bot-overrides-refresh-refused.json | 2 +- .../settings-bot-overrides-refused.json | 2 +- ...ettings-bot-overrides-transport-error.json | 2 +- .../goldens/settings-home-coalesced.json | 2 +- .../settings-home-providers-fulfilled.json | 2 +- ...ings-home-providers-refuse-after-data.json | 2 +- .../settings-home-providers-refused.json | 2 +- ...ttings-home-providers-transport-error.json | 2 +- .../goldens/settings-new-tab-refused.json | 2 +- .../goldens/settings-new-tab-ssh.json | 2 +- .../settings-new-tab-transport-error.json | 2 +- .../goldens/settings-repo-cache-expiry.json | 2 +- .../settings-repo-metadata-fulfilled.json | 2 +- .../goldens/settings-repo-metadata-icons.json | 469 +++++++++++++ ...tings-repo-metadata-refuse-after-data.json | 2 +- .../settings-repo-metadata-refused.json | 2 +- .../settings-repo-metadata-single-host.json | 2 +- ...ettings-repo-metadata-transport-error.json | 2 +- .../settings-resume-metadata-fulfilled.json | 2 +- ...ngs-resume-metadata-refuse-after-data.json | 2 +- .../settings-resume-metadata-refused.json | 2 +- ...tings-resume-metadata-transport-error.json | 2 +- .../settings-task-hydration-fulfilled.json | 2 +- ...ings-task-hydration-refuse-after-data.json | 2 +- .../settings-task-hydration-refused.json | 2 +- ...ttings-task-hydration-transport-error.json | 2 +- ...settings-task-workspace-create-linear.json | 2 +- ...-task-workspace-create-pr-start-point.json | 2 +- .../settings-task-workspace-fulfilled.json | 2 +- .../settings-task-workspace-refused.json | 2 +- ...ttings-task-workspace-transport-error.json | 2 +- .../goldens/settings-task-write.json | 2 +- .../settings-workspace-context-fulfilled.json | 2 +- ...s-workspace-context-refuse-after-data.json | 2 +- .../settings-workspace-context-refused.json | 2 +- ...ngs-workspace-context-transport-error.json | 2 +- .../settings-workspace-submit-fulfilled.json | 2 +- .../settings-workspace-submit-refused.json | 2 +- ...ings-workspace-submit-transport-error.json | 2 +- .../speech-audio-chunk-acknowledged.json | 2 +- .../speech-desktop-start-fulfilled.json | 2 +- ...speech-desktop-start-recording-failed.json | 2 +- .../speech-desktop-start-superseded.json | 2 +- .../speech-dictation-session-cancelled.json | 2 +- .../speech-dictation-session-transcript.json | 2 +- .../speech-setup-sheet-denied-to-mobile.json | 2 +- .../goldens/speech-setup-sheet-fulfilled.json | 2 +- .../speech-setup-sheet-legacy-desktop.json | 2 +- .../speech-setup-sheet-model-vocabulary.json | 164 +++++ .../structured-agent-session-created.json | 2 +- .../goldens/structured-launch-created.json | 2 +- .../structured-launch-definitive-refusal.json | 2 +- ...uctured-launch-replays-dropped-create.json | 2 +- .../structured-launch-support-refused.json | 2 +- .../structured-launch-unsupported.json | 2 +- .../goldens/tasks-route-repo-list.json | 2 +- .../terminal-gesture-flush-and-clear.json | 2 +- .../goldens/terminal-input-send-accepted.json | 2 +- .../goldens/terminal-input-send-refused.json | 2 +- .../goldens/terminal-live-input-accepted.json | 2 +- .../goldens/terminal-paste-accepted.json | 2 +- .../goldens/terminal-paste-refused.json | 2 +- .../terminal-query-reply-accepted.json | 2 +- .../terminal-query-reply-unsubscribed.json | 2 +- .../goldens/terminal-raw-input-refused.json | 2 +- .../goldens/terminal-raw-input-reported.json | 2 +- .../terminal-takeover-report-accepted.json | 2 +- .../terminal-takeover-report-retried.json | 2 +- .../terminal-viewport-refit-applied.json | 2 +- ...erminal-viewport-refit-legacy-desktop.json | 2 +- ...terminal-worktree-connection-resolved.json | 2 +- .../goldens/tk-create-github.json | 2 +- .../goldens/tk-create-gitlab.json | 2 +- .../goldens/tk-create-linear.json | 2 +- .../goldens/tk-item-checks-files.json | 2 +- .../goldens/tk-item-comment-github.json | 2 +- .../goldens/tk-item-comment-gitlab-mr.json | 2 +- .../goldens/tk-item-comment-gitlab.json | 2 +- .../tk-item-detail-github-reactions.json | 2 +- .../goldens/tk-item-detail-github.json | 2 +- .../tk-item-detail-gitlab-reactions.json | 2 +- .../goldens/tk-item-detail-gitlab.json | 2 +- .../goldens/tk-item-detail-linear.json | 2 +- .../goldens/tk-item-detail-metadata.json | 2 +- .../goldens/tk-item-merge-gitlab.json | 2 +- .../goldens/tk-item-metadata-github.json | 2 +- .../goldens/tk-item-metadata-gitlab-mr.json | 2 +- .../goldens/tk-item-metadata-gitlab.json | 2 +- .../goldens/tk-item-reply-merge.json | 2 +- .../goldens/tk-item-review-github.json | 2 +- .../goldens/tk-item-status-gitlab-mr.json | 2 +- .../goldens/tk-item-status-gitlab.json | 2 +- .../goldens/tk-linear-connect.json | 2 +- .../goldens/tk-linear-item.json | 2 +- .../goldens/tk-linear-team-context.json | 2 +- .../goldens/tk-list-gitlab-items.json | 2 +- .../goldens/tk-list-gitlab-todos.json | 2 +- .../goldens/tk-list-linear.json | 2 +- .../goldens/tk-project-board-load.json | 2 +- .../goldens/tk-project-repo-slugs.json | 2 +- .../tk-project-row-comments-issue.json | 2 +- .../goldens/tk-project-row-comments-pr.json | 2 +- .../goldens/tk-project-row-detail.json | 2 +- .../goldens/tk-project-row-fields.json | 2 +- .../goldens/tk-project-row-files-merge.json | 2 +- .../goldens/tk-project-row-metadata-load.json | 2 +- .../goldens/tk-project-row-review-checks.json | 2 +- .../goldens/tk-project-row-threads.json | 2 +- .../goldens/tk-provider-load.json | 2 +- ...-capability-probe-cutover-reasks-fast.json | 2 +- ...ty-probe-non-string-capabilities-drop.json | 2 +- .../transport-capability-probe-publishes.json | 2 +- ...rt-capability-probe-refused-backs-off.json | 2 +- ...-status-gates-drop-keeps-capabilities.json | 2 +- .../transport-host-status-gates-ready.json | 2 +- ...rt-host-status-gates-refused-degrades.json | 2 +- .../transport-pairing-race-both-refused.json | 2 +- ...t-pairing-race-direct-completes-first.json | 2 +- ...rt-pairing-race-relay-completes-first.json | 2 +- ...g-race-relay-wins-when-direct-refused.json | 2 +- .../goldens/tw-capabilities-advertised.json | 2 +- .../tw-capabilities-cutover-retried.json | 2 +- .../tw-capabilities-legacy-idempotency.json | 2 +- .../tw-create-retry-agent-launched.json | 2 +- .../tw-create-retry-ambiguous-after-drop.json | 2 +- ...reate-retry-ambiguous-while-connected.json | 2 +- ...e-retry-ambiguous-without-idempotency.json | 2 +- .../goldens/tw-create-retry-created.json | 2 +- .../tw-create-retry-name-collision.json | 2 +- .../tw-create-retry-unretryable-refusal.json | 2 +- .../goldens/tw-create-retry-warning-kept.json | 2 +- .../goldens/tw-hosted-base-resolved.json | 2 +- .../goldens/tw-hosted-base-soft-error.json | 2 +- .../goldens/tw-paste-lookup-resolved.json | 2 +- .../goldens/tw-paste-lookup-slug-refused.json | 2 +- .../tw-paste-lookup-slug-unsupported.json | 2 +- .../goldens/tw-setup-hook-trust-always.json | 2 +- .../goldens/tw-setup-hook-trust-approved.json | 2 +- .../tw-smart-search-all-providers.json | 2 +- ...tw-smart-search-gitlab-provider-error.json | 2 +- .../tw-smart-search-linear-listed.json | 2 +- .../tw-task-preferences-resume-write.json | 2 +- .../tw-workspace-source-presets-refused.json | 2 +- .../goldens/tw-workspace-source-presets.json | 2 +- .../tw-workspace-sparse-missing-preset.json | 2 +- .../goldens/tw-workspace-sparse-saved.json | 2 +- .../tw-workspace-ssh-connect-refused.json | 2 +- .../goldens/tw-workspace-ssh-connected.json | 2 +- .../tw-workspace-ssh-local-agents.json | 2 +- .../goldens/tw-workspace-ssh-not-ready.json | 2 +- .../worktree-catalog-snapshot-unreadable.json | 2 +- .../goldens/worktree-catalog-snapshot.json | 2 +- .../goldens/worktree-home-catalog.json | 2 +- .../goldens/worktree-retired-names.json | 2 +- mobile/rpc-foundation/pilot-scenarios.json | 347 +++++++++- .../agent-history-reply-schema.test.ts | 54 ++ .../agent-history-reply-schema.ts | 66 ++ .../mobile-agent-history-operations.ts | 20 +- .../use-mobile-agent-history-state.ts | 17 +- .../components/MobileDictationSetupSheet.tsx | 2 +- mobile/src/components/MobileRepoIcon.tsx | 6 +- mobile/src/components/VoiceModelList.tsx | 2 +- mobile/src/components/WorktreeListRow.tsx | 4 +- .../dictation/dictation-reply-schema.test.ts | 103 +++ .../src/dictation/dictation-reply-schema.ts | 93 +++ .../dictation/mobile-dictation-operations.ts | 25 +- .../dictation/mobile-dictation-setup.test.ts | 40 +- .../src/dictation/mobile-dictation-setup.ts | 17 +- mobile/src/files/MobileFileExplorerPanel.tsx | 15 +- .../files/file-explorer-reply-schema.test.ts | 50 ++ .../src/files/file-explorer-reply-schema.ts | 45 ++ mobile/src/files/file-list-fallback.ts | 6 +- .../files/file-ownership-reply-schema.test.ts | 51 ++ .../src/files/file-ownership-reply-schema.ts | 53 ++ .../files/file-preview-reply-schema.test.ts | 70 ++ mobile/src/files/file-preview-reply-schema.ts | 92 +++ .../files/file-tab-doc-reply-schema.test.ts | 76 +++ mobile/src/files/file-tab-doc-reply-schema.ts | 96 +++ mobile/src/files/mobile-diff-image-preview.ts | 4 +- .../files/mobile-file-explorer-operations.ts | 11 +- .../mobile-file-mutation-ownership.test.ts | 25 + .../files/mobile-file-mutation-ownership.ts | 19 +- .../files/mobile-file-ownership-operations.ts | 14 +- .../files/mobile-file-preview-operations.ts | 33 +- .../files/mobile-file-preview-request.test.ts | 10 +- .../files/mobile-file-tab-doc-operations.ts | 32 +- mobile/src/files/mobile-file-tab-doc.ts | 21 +- .../src/host-screen/host-screen-operations.ts | 31 +- .../host-screen-reply-schema.test.ts | 175 +++++ .../host-screen/host-screen-reply-schema.ts | 181 +++++ .../src/host-screen/use-host-repo-metadata.ts | 52 +- .../src/host-screen/use-host-screen-state.ts | 4 +- .../src/session/MobileNativeChatComposer.tsx | 2 +- .../src/session/MobileNativeChatOverlay.tsx | 2 +- mobile/src/session/MobileNativeChatView.tsx | 2 +- .../session/MobileTerminalInputActions.tsx | 2 +- .../session/mobile-markdown-disk-fallback.ts | 4 +- .../mobile-session-route-parity.test.ts | 8 +- .../use-mobile-session-document-readers.ts | 7 +- .../use-mobile-session-screen-state.ts | 4 +- .../mutants/operation-mutations.ts | 16 +- .../mutants/reply-schema-mutations.ts | 89 +++ .../transport/mobile-runtime-host-platform.ts | 31 +- .../settings-read-operations.test.ts | 24 + .../unchecked-rpc-reader-boundary.test.ts | 8 +- .../unchecked-rpc-reader-inventory.ts | 39 +- .../worktree/worktree-host-context-labels.ts | 3 +- src/shared/execution-host.ts | 9 +- src/shared/zod-salvage.ts | 12 + 833 files changed, 7381 insertions(+), 1971 deletions(-) create mode 100644 mobile/rpc-foundation/goldens/files-preview-artifact-image-read.json create mode 100644 mobile/rpc-foundation/goldens/files-preview-worktree-image-read.json create mode 100644 mobile/rpc-foundation/goldens/files-preview-worktree-text-read.json create mode 100644 mobile/rpc-foundation/goldens/matrix-files.preview-artifact-image-files.readterminalartifactpreview-1.json create mode 100644 mobile/rpc-foundation/goldens/matrix-files.preview-worktree-image-files.readpreview-1.json create mode 100644 mobile/rpc-foundation/goldens/matrix-files.preview-worktree-text-files.read-1.json create mode 100644 mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-files.read-1.json create mode 100644 mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-markdown.readtab-1.json create mode 100644 mobile/rpc-foundation/goldens/session-markdown-disk-read.json create mode 100644 mobile/rpc-foundation/goldens/session-markdown-disk-served.json create mode 100644 mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json create mode 100644 mobile/rpc-foundation/goldens/speech-setup-sheet-model-vocabulary.json create mode 100644 mobile/src/agent-history/agent-history-reply-schema.test.ts create mode 100644 mobile/src/agent-history/agent-history-reply-schema.ts create mode 100644 mobile/src/dictation/dictation-reply-schema.test.ts create mode 100644 mobile/src/dictation/dictation-reply-schema.ts create mode 100644 mobile/src/files/file-explorer-reply-schema.test.ts create mode 100644 mobile/src/files/file-explorer-reply-schema.ts create mode 100644 mobile/src/files/file-ownership-reply-schema.test.ts create mode 100644 mobile/src/files/file-ownership-reply-schema.ts create mode 100644 mobile/src/files/file-preview-reply-schema.test.ts create mode 100644 mobile/src/files/file-preview-reply-schema.ts create mode 100644 mobile/src/files/file-tab-doc-reply-schema.test.ts create mode 100644 mobile/src/files/file-tab-doc-reply-schema.ts create mode 100644 mobile/src/host-screen/host-screen-reply-schema.test.ts create mode 100644 mobile/src/host-screen/host-screen-reply-schema.ts create mode 100644 mobile/src/test-support/rpc-recording/mutants/reply-schema-mutations.ts diff --git a/mobile/rpc-foundation/goldens/aivault-history-scan-fulfilled.json b/mobile/rpc-foundation/goldens/aivault-history-scan-fulfilled.json index a1f0b150d42..9a29e3dba5b 100644 --- a/mobile/rpc-foundation/goldens/aivault-history-scan-fulfilled.json +++ b/mobile/rpc-foundation/goldens/aivault-history-scan-fulfilled.json @@ -3,7 +3,7 @@ "family": "aiVault.history", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7add46922ba5486e56c8acd99a53d083b605f5d98f3cc44ee3cb350ec0406080", diff --git a/mobile/rpc-foundation/goldens/aivault-history-scan-unsupported.json b/mobile/rpc-foundation/goldens/aivault-history-scan-unsupported.json index 1a8fe04ab3b..6713242ea22 100644 --- a/mobile/rpc-foundation/goldens/aivault-history-scan-unsupported.json +++ b/mobile/rpc-foundation/goldens/aivault-history-scan-unsupported.json @@ -3,7 +3,7 @@ "family": "aiVault.history", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7add46922ba5486e56c8acd99a53d083b605f5d98f3cc44ee3cb350ec0406080", diff --git a/mobile/rpc-foundation/goldens/aivault-history-scan-worktrees-late.json b/mobile/rpc-foundation/goldens/aivault-history-scan-worktrees-late.json index 8fc3d162666..219d33bd556 100644 --- a/mobile/rpc-foundation/goldens/aivault-history-scan-worktrees-late.json +++ b/mobile/rpc-foundation/goldens/aivault-history-scan-worktrees-late.json @@ -3,7 +3,7 @@ "family": "aiVault.history", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7add46922ba5486e56c8acd99a53d083b605f5d98f3cc44ee3cb350ec0406080", diff --git a/mobile/rpc-foundation/goldens/aivault-history-screen-listed.json b/mobile/rpc-foundation/goldens/aivault-history-screen-listed.json index bec79adb1a3..bdd65353a52 100644 --- a/mobile/rpc-foundation/goldens/aivault-history-screen-listed.json +++ b/mobile/rpc-foundation/goldens/aivault-history-screen-listed.json @@ -3,7 +3,7 @@ "family": "aiVault.history-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c78ab47eea594b7e1988403321ff9ba135ca60c513bb9d5848bdde26c0ffe3c3", diff --git a/mobile/rpc-foundation/goldens/aivault-history-screen-worktrees.json b/mobile/rpc-foundation/goldens/aivault-history-screen-worktrees.json index e69e5ec0b49..2e8344adf55 100644 --- a/mobile/rpc-foundation/goldens/aivault-history-screen-worktrees.json +++ b/mobile/rpc-foundation/goldens/aivault-history-screen-worktrees.json @@ -3,7 +3,7 @@ "family": "aiVault.history-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c78ab47eea594b7e1988403321ff9ba135ca60c513bb9d5848bdde26c0ffe3c3", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-launch-create-refused.json b/mobile/rpc-foundation/goldens/aivault-resume-launch-create-refused.json index 49c05a1008d..8e84600879e 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-launch-create-refused.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-launch-create-refused.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-launch-invalid-tab.json b/mobile/rpc-foundation/goldens/aivault-resume-launch-invalid-tab.json index 10d9f91ea24..7fbf437c002 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-launch-invalid-tab.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-launch-invalid-tab.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-launch-locked.json b/mobile/rpc-foundation/goldens/aivault-resume-launch-locked.json index 1e526c77a59..83ba9f92631 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-launch-locked.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-launch-locked.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-launch-sent.json b/mobile/rpc-foundation/goldens/aivault-resume-launch-sent.json index a14ea6cf1ca..859040920ac 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-launch-sent.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-launch-sent.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-prepare-refused.json b/mobile/rpc-foundation/goldens/aivault-resume-prepare-refused.json index 994c6e6bbd8..a33a45fdc08 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-prepare-refused.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-prepare-refused.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-prepare-repin.json b/mobile/rpc-foundation/goldens/aivault-resume-prepare-repin.json index 93252c68b03..bfd5aa01e33 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-prepare-repin.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-prepare-repin.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-prepare-skipped.json b/mobile/rpc-foundation/goldens/aivault-resume-prepare-skipped.json index 73cd43371a7..0c2fe6c55d0 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-prepare-skipped.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-prepare-skipped.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-prepare-unavailable.json b/mobile/rpc-foundation/goldens/aivault-resume-prepare-unavailable.json index f20d528761f..285cc03c20a 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-prepare-unavailable.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-prepare-unavailable.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/b1.json b/mobile/rpc-foundation/goldens/b1.json index 1bfd4d16dec..4b31f46ce10 100644 --- a/mobile/rpc-foundation/goldens/b1.json +++ b/mobile/rpc-foundation/goldens/b1.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/b2.json b/mobile/rpc-foundation/goldens/b2.json index c4106dbb1a3..92431367d93 100644 --- a/mobile/rpc-foundation/goldens/b2.json +++ b/mobile/rpc-foundation/goldens/b2.json @@ -3,7 +3,7 @@ "family": "project-explicit-false", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/b3.json b/mobile/rpc-foundation/goldens/b3.json index f0b0c4bec9e..0d6f3d2be58 100644 --- a/mobile/rpc-foundation/goldens/b3.json +++ b/mobile/rpc-foundation/goldens/b3.json @@ -3,7 +3,7 @@ "family": "linear-detail-barrier", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/browser-dialog-accepted.json b/mobile/rpc-foundation/goldens/browser-dialog-accepted.json index 5a92cf0a421..fadc8f3dc2d 100644 --- a/mobile/rpc-foundation/goldens/browser-dialog-accepted.json +++ b/mobile/rpc-foundation/goldens/browser-dialog-accepted.json @@ -3,7 +3,7 @@ "family": "browser.dialog", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/browser-dialog-dismissed.json b/mobile/rpc-foundation/goldens/browser-dialog-dismissed.json index 049b365ef67..170a43574d2 100644 --- a/mobile/rpc-foundation/goldens/browser-dialog-dismissed.json +++ b/mobile/rpc-foundation/goldens/browser-dialog-dismissed.json @@ -3,7 +3,7 @@ "family": "browser.dialog", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/browser-keyboard-input.json b/mobile/rpc-foundation/goldens/browser-keyboard-input.json index 2c87410c7ec..65bc5709394 100644 --- a/mobile/rpc-foundation/goldens/browser-keyboard-input.json +++ b/mobile/rpc-foundation/goldens/browser-keyboard-input.json @@ -3,7 +3,7 @@ "family": "browser.keyboard", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/browser-pointer-click-accepted.json b/mobile/rpc-foundation/goldens/browser-pointer-click-accepted.json index c42fc9d81ae..2fb934ac18c 100644 --- a/mobile/rpc-foundation/goldens/browser-pointer-click-accepted.json +++ b/mobile/rpc-foundation/goldens/browser-pointer-click-accepted.json @@ -3,7 +3,7 @@ "family": "browser.pointer-click", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/browser-pointer-click-fallback.json b/mobile/rpc-foundation/goldens/browser-pointer-click-fallback.json index 2811788fb67..e3953d5078c 100644 --- a/mobile/rpc-foundation/goldens/browser-pointer-click-fallback.json +++ b/mobile/rpc-foundation/goldens/browser-pointer-click-fallback.json @@ -3,7 +3,7 @@ "family": "browser.pointer-click", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/browser-wheel-scrolled.json b/mobile/rpc-foundation/goldens/browser-wheel-scrolled.json index ba4eaae2f0d..2118fe8274b 100644 --- a/mobile/rpc-foundation/goldens/browser-wheel-scrolled.json +++ b/mobile/rpc-foundation/goldens/browser-wheel-scrolled.json @@ -3,7 +3,7 @@ "family": "browser.wheel", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-attachment-anonymous.json b/mobile/rpc-foundation/goldens/clipboard-image-attachment-anonymous.json index 18fe9732bdd..39d26f72c18 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-attachment-anonymous.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-attachment-anonymous.json @@ -3,7 +3,7 @@ "family": "clipboard.image-attachment", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-attachment-blocked-before-send.json b/mobile/rpc-foundation/goldens/clipboard-image-attachment-blocked-before-send.json index 35294e65642..fdd29e642fb 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-attachment-blocked-before-send.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-attachment-blocked-before-send.json @@ -3,7 +3,7 @@ "family": "clipboard.image-attachment", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-attachment-cancelled.json b/mobile/rpc-foundation/goldens/clipboard-image-attachment-cancelled.json index 49dfe6bd8b9..060e98a0cd5 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-attachment-cancelled.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-attachment-cancelled.json @@ -3,7 +3,7 @@ "family": "clipboard.image-attachment", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-attachment-pasted.json b/mobile/rpc-foundation/goldens/clipboard-image-attachment-pasted.json index 98215f436b2..a7608a934cf 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-attachment-pasted.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-attachment-pasted.json @@ -3,7 +3,7 @@ "family": "clipboard.image-attachment", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-attachment-upload-refused.json b/mobile/rpc-foundation/goldens/clipboard-image-attachment-upload-refused.json index a3bdb5c3b42..d5d370accc3 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-attachment-upload-refused.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-attachment-upload-refused.json @@ -3,7 +3,7 @@ "family": "clipboard.image-attachment", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-upload-aborts-on-chunk-failure.json b/mobile/rpc-foundation/goldens/clipboard-image-upload-aborts-on-chunk-failure.json index 7638c537ac0..f0ddb7f1646 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-upload-aborts-on-chunk-failure.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-upload-aborts-on-chunk-failure.json @@ -3,7 +3,7 @@ "family": "clipboard.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-upload-chunked.json b/mobile/rpc-foundation/goldens/clipboard-image-upload-chunked.json index 6adda9bd1e3..fbb47aa7e9b 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-upload-chunked.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-upload-chunked.json @@ -3,7 +3,7 @@ "family": "clipboard.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-upload-single-frame-fallback.json b/mobile/rpc-foundation/goldens/clipboard-image-upload-single-frame-fallback.json index 950ebfd54f2..689725d00fb 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-upload-single-frame-fallback.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-upload-single-frame-fallback.json @@ -3,7 +3,7 @@ "family": "clipboard.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-upload-start-refused.json b/mobile/rpc-foundation/goldens/clipboard-image-upload-start-refused.json index 7706b705cfc..5c2fc25301f 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-upload-start-refused.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-upload-start-refused.json @@ -3,7 +3,7 @@ "family": "clipboard.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/codex-reset-credit-consumed.json b/mobile/rpc-foundation/goldens/codex-reset-credit-consumed.json index c12807026d8..d44a3a46cd4 100644 --- a/mobile/rpc-foundation/goldens/codex-reset-credit-consumed.json +++ b/mobile/rpc-foundation/goldens/codex-reset-credit-consumed.json @@ -3,7 +3,7 @@ "family": "components.codex-reset-credit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "76b53dea504a688493843e23e8e7d052fc196f559bb1e2579d0fd38bf3156d0e", diff --git a/mobile/rpc-foundation/goldens/codex-reset-credit-resumed.json b/mobile/rpc-foundation/goldens/codex-reset-credit-resumed.json index e0342bc2b5a..0041e246aa7 100644 --- a/mobile/rpc-foundation/goldens/codex-reset-credit-resumed.json +++ b/mobile/rpc-foundation/goldens/codex-reset-credit-resumed.json @@ -3,7 +3,7 @@ "family": "components.codex-reset-credit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "76b53dea504a688493843e23e8e7d052fc196f559bb1e2579d0fd38bf3156d0e", diff --git a/mobile/rpc-foundation/goldens/components-codex-capability.json b/mobile/rpc-foundation/goldens/components-codex-capability.json index 02b2f79ccf5..0a671ae86d7 100644 --- a/mobile/rpc-foundation/goldens/components-codex-capability.json +++ b/mobile/rpc-foundation/goldens/components-codex-capability.json @@ -3,7 +3,7 @@ "family": "components.codex-reset-capability", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/components-setup-ask.json b/mobile/rpc-foundation/goldens/components-setup-ask.json index 1fda7000207..901a2b44a31 100644 --- a/mobile/rpc-foundation/goldens/components-setup-ask.json +++ b/mobile/rpc-foundation/goldens/components-setup-ask.json @@ -3,7 +3,7 @@ "family": "components.setup-script", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/components-target-local.json b/mobile/rpc-foundation/goldens/components-target-local.json index 8c102276b32..74d97414930 100644 --- a/mobile/rpc-foundation/goldens/components-target-local.json +++ b/mobile/rpc-foundation/goldens/components-target-local.json @@ -3,7 +3,7 @@ "family": "components.execution-target-local", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/components-target-ssh.json b/mobile/rpc-foundation/goldens/components-target-ssh.json index 3e5fbc34416..1f581e9b244 100644 --- a/mobile/rpc-foundation/goldens/components-target-ssh.json +++ b/mobile/rpc-foundation/goldens/components-target-ssh.json @@ -3,7 +3,7 @@ "family": "components.execution-target", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/diff-review-branch-compare.json b/mobile/rpc-foundation/goldens/diff-review-branch-compare.json index 1d97c9a77a4..98723436c85 100644 --- a/mobile/rpc-foundation/goldens/diff-review-branch-compare.json +++ b/mobile/rpc-foundation/goldens/diff-review-branch-compare.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/diff-review-branch-file-diff.json b/mobile/rpc-foundation/goldens/diff-review-branch-file-diff.json index bdbc3f75bf3..f2721d14600 100644 --- a/mobile/rpc-foundation/goldens/diff-review-branch-file-diff.json +++ b/mobile/rpc-foundation/goldens/diff-review-branch-file-diff.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/diff-review-notes-refused-before-compare.json b/mobile/rpc-foundation/goldens/diff-review-notes-refused-before-compare.json index e4c2d2d8313..9b229f7e6dd 100644 --- a/mobile/rpc-foundation/goldens/diff-review-notes-refused-before-compare.json +++ b/mobile/rpc-foundation/goldens/diff-review-notes-refused-before-compare.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/diff-review-refused-file-diff.json b/mobile/rpc-foundation/goldens/diff-review-refused-file-diff.json index 9a638007a34..56095e4a63e 100644 --- a/mobile/rpc-foundation/goldens/diff-review-refused-file-diff.json +++ b/mobile/rpc-foundation/goldens/diff-review-refused-file-diff.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/diff-review-snapshot.json b/mobile/rpc-foundation/goldens/diff-review-snapshot.json index 05da9203d3a..57ab3b50be0 100644 --- a/mobile/rpc-foundation/goldens/diff-review-snapshot.json +++ b/mobile/rpc-foundation/goldens/diff-review-snapshot.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/diff-review-status-unavailable.json b/mobile/rpc-foundation/goldens/diff-review-status-unavailable.json index 2e3ed0a70f0..8d7b5fabb67 100644 --- a/mobile/rpc-foundation/goldens/diff-review-status-unavailable.json +++ b/mobile/rpc-foundation/goldens/diff-review-status-unavailable.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/diff-review-worktree-file-diff.json b/mobile/rpc-foundation/goldens/diff-review-worktree-file-diff.json index 670bb312449..8fcd52dcc00 100644 --- a/mobile/rpc-foundation/goldens/diff-review-worktree-file-diff.json +++ b/mobile/rpc-foundation/goldens/diff-review-worktree-file-diff.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/file-tap-open-refused.json b/mobile/rpc-foundation/goldens/file-tap-open-refused.json index cc0555b1f91..7dcbb6b64a9 100644 --- a/mobile/rpc-foundation/goldens/file-tap-open-refused.json +++ b/mobile/rpc-foundation/goldens/file-tap-open-refused.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/file-tap-opens-worktree-file.json b/mobile/rpc-foundation/goldens/file-tap-opens-worktree-file.json index 164436d189c..50b84efd16f 100644 --- a/mobile/rpc-foundation/goldens/file-tap-opens-worktree-file.json +++ b/mobile/rpc-foundation/goldens/file-tap-opens-worktree-file.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/file-tap-previews-absolute-artifact.json b/mobile/rpc-foundation/goldens/file-tap-previews-absolute-artifact.json index 68400bc1aaf..9c1f4275db1 100644 --- a/mobile/rpc-foundation/goldens/file-tap-previews-absolute-artifact.json +++ b/mobile/rpc-foundation/goldens/file-tap-previews-absolute-artifact.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/file-tap-resolve-miss.json b/mobile/rpc-foundation/goldens/file-tap-resolve-miss.json index f5bbcdbffc8..a854453e156 100644 --- a/mobile/rpc-foundation/goldens/file-tap-resolve-miss.json +++ b/mobile/rpc-foundation/goldens/file-tap-resolve-miss.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/file-tap-resolve-refused.json b/mobile/rpc-foundation/goldens/file-tap-resolve-refused.json index 5890276c472..af42db14f77 100644 --- a/mobile/rpc-foundation/goldens/file-tap-resolve-refused.json +++ b/mobile/rpc-foundation/goldens/file-tap-resolve-refused.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/files-explorer-legacy-fallback.json b/mobile/rpc-foundation/goldens/files-explorer-legacy-fallback.json index 3bcac330cd1..6e60676bd49 100644 --- a/mobile/rpc-foundation/goldens/files-explorer-legacy-fallback.json +++ b/mobile/rpc-foundation/goldens/files-explorer-legacy-fallback.json @@ -3,7 +3,7 @@ "family": "files.explorer-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7a42a348f4407b94cf75ac77a4b6b783b7d28103b1ae1d111d2708dab0dd4a0c", diff --git a/mobile/rpc-foundation/goldens/files-explorer-readdir.json b/mobile/rpc-foundation/goldens/files-explorer-readdir.json index 8cd7d414207..ebc75852848 100644 --- a/mobile/rpc-foundation/goldens/files-explorer-readdir.json +++ b/mobile/rpc-foundation/goldens/files-explorer-readdir.json @@ -3,7 +3,7 @@ "family": "files.explorer-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7a42a348f4407b94cf75ac77a4b6b783b7d28103b1ae1d111d2708dab0dd4a0c", diff --git a/mobile/rpc-foundation/goldens/files-ownership-local.json b/mobile/rpc-foundation/goldens/files-ownership-local.json index 39c73196456..c77d585bf75 100644 --- a/mobile/rpc-foundation/goldens/files-ownership-local.json +++ b/mobile/rpc-foundation/goldens/files-ownership-local.json @@ -3,7 +3,7 @@ "family": "files.mutation-ownership", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-ownership-ssh.json b/mobile/rpc-foundation/goldens/files-ownership-ssh.json index bbe1b7a7fce..14649405161 100644 --- a/mobile/rpc-foundation/goldens/files-ownership-ssh.json +++ b/mobile/rpc-foundation/goldens/files-ownership-ssh.json @@ -3,7 +3,7 @@ "family": "files.mutation-ownership", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-artifact-direct.json b/mobile/rpc-foundation/goldens/files-preview-artifact-direct.json index 03709d3cb14..a5c4b4f7936 100644 --- a/mobile/rpc-foundation/goldens/files-preview-artifact-direct.json +++ b/mobile/rpc-foundation/goldens/files-preview-artifact-direct.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-artifact-image-read.json b/mobile/rpc-foundation/goldens/files-preview-artifact-image-read.json new file mode 100644 index 00000000000..a5ff1cfdeb4 --- /dev/null +++ b/mobile/rpc-foundation/goldens/files-preview-artifact-image-read.json @@ -0,0 +1,95 @@ +{ + "operation": "files.preview-load", + "family": "files.preview-artifact-image", + "namedDeltas": [], + "runnerVersion": 1, + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", + "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", + "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", + "scenarioSha256": "39fe4bce80a2323dbc276235daab48e7fe55d1fdb9c1ea1eeac7c17bceb8df18", + "platform": "darwin", + "scenarioVersion": 1, + "projectionVersion": 2, + "goldenFormatVersion": 5, + "values": { + "7659b8b575da": { + "preview": { + "dataUri": "data:image/png;base64,aGk=", + "kind": "image", + "status": "ready" + } + }, + "b5608d3d00c8": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 2, + "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"files.readTerminalArtifactPreview\",\"params\":{\"worktree\":\"id:workspace-1\",\"absolutePath\":\"/logs/shot.png\",\"grantId\":\"grant-1\"}}" + }, + "ce511c0ab8ac": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readTerminalArtifactPreview" + }, + { + "name": "params", + "value": { + "absolutePath": "/logs/shot.png", + "grantId": "grant-1", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "content": "aGk=", + "isBinary": true, + "isImage": true, + "mimeType": "image/png" + } + } + } + }, + "eee847a9d90d": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "dataUri": "data:image/png;base64,aGk=", + "kind": "image", + "status": "ready" + } + } + }, + "recording": { + "scenario": "files-preview-artifact-image-read", + "checkpoints": [ + { + "id": "settled", + "observation": { + "sender": ["ce511c0ab8ac"], + "payloads": ["b5608d3d00c8"], + "settlements": { + "load": "eee847a9d90d" + }, + "state": "7659b8b575da", + "effects": [] + } + } + ] + } +} diff --git a/mobile/rpc-foundation/goldens/files-preview-artifact-image.json b/mobile/rpc-foundation/goldens/files-preview-artifact-image.json index 11004a55d31..812372d4b3d 100644 --- a/mobile/rpc-foundation/goldens/files-preview-artifact-image.json +++ b/mobile/rpc-foundation/goldens/files-preview-artifact-image.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-grant-refresh.json b/mobile/rpc-foundation/goldens/files-preview-grant-refresh.json index 92fb6a71365..0ecf16d4782 100644 --- a/mobile/rpc-foundation/goldens/files-preview-grant-refresh.json +++ b/mobile/rpc-foundation/goldens/files-preview-grant-refresh.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-worktree-image-read.json b/mobile/rpc-foundation/goldens/files-preview-worktree-image-read.json new file mode 100644 index 00000000000..4c92b29c631 --- /dev/null +++ b/mobile/rpc-foundation/goldens/files-preview-worktree-image-read.json @@ -0,0 +1,94 @@ +{ + "operation": "files.preview-load", + "family": "files.preview-worktree-image", + "namedDeltas": [], + "runnerVersion": 1, + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", + "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", + "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", + "scenarioSha256": "767af05bb58756f4adc960b95ee6547cc28145c7f6c7c6a4b27582aed9741e37", + "platform": "darwin", + "scenarioVersion": 1, + "projectionVersion": 2, + "goldenFormatVersion": 5, + "values": { + "70f782fa6ef2": { + "name": "files.readPreview#1", + "ordinal": 2, + "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"files.readPreview\",\"params\":{\"worktree\":\"id:workspace-1\",\"relativePath\":\"docs/logo.png\"}}" + }, + "7659b8b575da": { + "preview": { + "dataUri": "data:image/png;base64,aGk=", + "kind": "image", + "status": "ready" + } + }, + "a568af21f540": { + "name": "files.readPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readPreview" + }, + { + "name": "params", + "value": { + "relativePath": "docs/logo.png", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "content": "aGk=", + "isBinary": true, + "isImage": true, + "mimeType": "image/png" + } + } + } + }, + "eee847a9d90d": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "dataUri": "data:image/png;base64,aGk=", + "kind": "image", + "status": "ready" + } + } + }, + "recording": { + "scenario": "files-preview-worktree-image-read", + "checkpoints": [ + { + "id": "settled", + "observation": { + "sender": ["a568af21f540"], + "payloads": ["70f782fa6ef2"], + "settlements": { + "load": "eee847a9d90d" + }, + "state": "7659b8b575da", + "effects": [] + } + } + ] + } +} diff --git a/mobile/rpc-foundation/goldens/files-preview-worktree-image.json b/mobile/rpc-foundation/goldens/files-preview-worktree-image.json index 790d0a8416d..5f9df391ae8 100644 --- a/mobile/rpc-foundation/goldens/files-preview-worktree-image.json +++ b/mobile/rpc-foundation/goldens/files-preview-worktree-image.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-worktree-text-read.json b/mobile/rpc-foundation/goldens/files-preview-worktree-text-read.json new file mode 100644 index 00000000000..62768b8c8aa --- /dev/null +++ b/mobile/rpc-foundation/goldens/files-preview-worktree-text-read.json @@ -0,0 +1,97 @@ +{ + "operation": "files.preview-load", + "family": "files.preview-worktree-text", + "namedDeltas": [], + "runnerVersion": 1, + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", + "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", + "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", + "scenarioSha256": "21c3c61a18939873157f74c8d2d28bceae01abc702565af1ca897ab7304cb93c", + "platform": "darwin", + "scenarioVersion": 1, + "projectionVersion": 2, + "goldenFormatVersion": 5, + "values": { + "1cf4496bc8c0": { + "name": "files.read#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "byteLength": 8, + "content": "# readme", + "truncated": false + } + } + } + }, + "3f8bf3069e3d": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "byteLength": 8, + "content": "# readme", + "kind": "markdown", + "status": "ready", + "truncated": false + } + }, + "47ef2e397e18": { + "preview": { + "byteLength": 8, + "content": "# readme", + "kind": "markdown", + "status": "ready", + "truncated": false + } + }, + "8423ff93fda2": { + "name": "files.read#1", + "ordinal": 2, + "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"files.read\",\"params\":{\"worktree\":\"id:workspace-1\",\"relativePath\":\"docs/readme.md\"}}" + } + }, + "recording": { + "scenario": "files-preview-worktree-text-read", + "checkpoints": [ + { + "id": "settled", + "observation": { + "sender": ["1cf4496bc8c0"], + "payloads": ["8423ff93fda2"], + "settlements": { + "load": "3f8bf3069e3d" + }, + "state": "47ef2e397e18", + "effects": [] + } + } + ] + } +} diff --git a/mobile/rpc-foundation/goldens/files-preview-worktree.json b/mobile/rpc-foundation/goldens/files-preview-worktree.json index dff7b0b3e9c..1c5dc8e052e 100644 --- a/mobile/rpc-foundation/goldens/files-preview-worktree.json +++ b/mobile/rpc-foundation/goldens/files-preview-worktree.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-save-blind.json b/mobile/rpc-foundation/goldens/files-save-blind.json index a197e99902d..a344040d975 100644 --- a/mobile/rpc-foundation/goldens/files-save-blind.json +++ b/mobile/rpc-foundation/goldens/files-save-blind.json @@ -3,7 +3,7 @@ "family": "files.preview-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-save-verified.json b/mobile/rpc-foundation/goldens/files-save-verified.json index e91ead6d80f..66a8317c631 100644 --- a/mobile/rpc-foundation/goldens/files-save-verified.json +++ b/mobile/rpc-foundation/goldens/files-save-verified.json @@ -3,7 +3,7 @@ "family": "files.preview-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-tab-doc-shapes.json b/mobile/rpc-foundation/goldens/files-tab-doc-shapes.json index ca757e6344a..90567841aca 100644 --- a/mobile/rpc-foundation/goldens/files-tab-doc-shapes.json +++ b/mobile/rpc-foundation/goldens/files-tab-doc-shapes.json @@ -3,7 +3,7 @@ "family": "files.tab-doc", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/home-host-accounts.json b/mobile/rpc-foundation/goldens/home-host-accounts.json index dd399a534da..ec72f151127 100644 --- a/mobile/rpc-foundation/goldens/home-host-accounts.json +++ b/mobile/rpc-foundation/goldens/home-host-accounts.json @@ -3,7 +3,7 @@ "family": "home.host-accounts", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c632fdbc4b730777ecb09f08bec14cca0586042b01ed99d40d0228806c7def4a", diff --git a/mobile/rpc-foundation/goldens/home-host-stats.json b/mobile/rpc-foundation/goldens/home-host-stats.json index f2c1f9b32c7..9e7db3c70f1 100644 --- a/mobile/rpc-foundation/goldens/home-host-stats.json +++ b/mobile/rpc-foundation/goldens/home-host-stats.json @@ -3,7 +3,7 @@ "family": "home.host-stats", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a9e0780298a1443664e7ae02056168aa34d67556c9c056d51a82c7b4a73ad35b", diff --git a/mobile/rpc-foundation/goldens/host-view-settings-sync.json b/mobile/rpc-foundation/goldens/host-view-settings-sync.json index 96dd5de9457..15288aa2ae4 100644 --- a/mobile/rpc-foundation/goldens/host-view-settings-sync.json +++ b/mobile/rpc-foundation/goldens/host-view-settings-sync.json @@ -3,7 +3,7 @@ "family": "host.view-settings", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a9e0780298a1443664e7ae02056168aa34d67556c9c056d51a82c7b4a73ad35b", diff --git a/mobile/rpc-foundation/goldens/host-worktree-actions-pin-open-delete.json b/mobile/rpc-foundation/goldens/host-worktree-actions-pin-open-delete.json index 13890d52477..dc5c84a2aa9 100644 --- a/mobile/rpc-foundation/goldens/host-worktree-actions-pin-open-delete.json +++ b/mobile/rpc-foundation/goldens/host-worktree-actions-pin-open-delete.json @@ -3,7 +3,7 @@ "family": "host.worktree-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "92c29bd78ca0c0d5917e9386fc447bb9a1698b1d1ffaba0db7546eaac60da639", diff --git a/mobile/rpc-foundation/goldens/host-worktree-delete-refused.json b/mobile/rpc-foundation/goldens/host-worktree-delete-refused.json index f6fbefe7a73..12f83c121e3 100644 --- a/mobile/rpc-foundation/goldens/host-worktree-delete-refused.json +++ b/mobile/rpc-foundation/goldens/host-worktree-delete-refused.json @@ -3,7 +3,7 @@ "family": "host.worktree-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "92c29bd78ca0c0d5917e9386fc447bb9a1698b1d1ffaba0db7546eaac60da639", diff --git a/mobile/rpc-foundation/goldens/host-worktree-refresh-stream.json b/mobile/rpc-foundation/goldens/host-worktree-refresh-stream.json index efa04b4a345..ce404e225cc 100644 --- a/mobile/rpc-foundation/goldens/host-worktree-refresh-stream.json +++ b/mobile/rpc-foundation/goldens/host-worktree-refresh-stream.json @@ -3,7 +3,7 @@ "family": "host-worktree-refresh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.json b/mobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.json index 80a7ecb0117..7ed8fd34921 100644 --- a/mobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.json +++ b/mobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.json b/mobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.json index 658f7e5cf2d..89322e5507b 100644 --- a/mobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.json +++ b/mobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/inventory-lifecycle.json b/mobile/rpc-foundation/goldens/inventory-lifecycle.json index c60d4fb16df..ffd9220457f 100644 --- a/mobile/rpc-foundation/goldens/inventory-lifecycle.json +++ b/mobile/rpc-foundation/goldens/inventory-lifecycle.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/inventory-repeat-query.json b/mobile/rpc-foundation/goldens/inventory-repeat-query.json index 8eec5ac7368..af2eee99e52 100644 --- a/mobile/rpc-foundation/goldens/inventory-repeat-query.json +++ b/mobile/rpc-foundation/goldens/inventory-repeat-query.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/lifecycle-b3.json b/mobile/rpc-foundation/goldens/lifecycle-b3.json index b7c7c228541..1146603393f 100644 --- a/mobile/rpc-foundation/goldens/lifecycle-b3.json +++ b/mobile/rpc-foundation/goldens/lifecycle-b3.json @@ -3,7 +3,7 @@ "family": "linear-detail-barrier", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.json b/mobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.json index b504be2692d..67113a20bb6 100644 --- a/mobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.json +++ b/mobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.json b/mobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.json index 5be84f3dc70..993915d8c1d 100644 --- a/mobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.json +++ b/mobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json b/mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json index 70dbfd9adad..5912fb5c06b 100644 --- a/mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json +++ b/mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json b/mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json index 9c46f0529bd..eee35dbaa4e 100644 --- a/mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json +++ b/mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/linear-select-workspace.json b/mobile/rpc-foundation/goldens/linear-select-workspace.json index 88e10a16286..90fdefa4091 100644 --- a/mobile/rpc-foundation/goldens/linear-select-workspace.json +++ b/mobile/rpc-foundation/goldens/linear-select-workspace.json @@ -3,7 +3,7 @@ "family": "linear.select-workspace-picker", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b65996c152b632d553a31e07e31ea5c76f998eada42cf0966923d730da21908f", diff --git a/mobile/rpc-foundation/goldens/live-worktree-name-stream.json b/mobile/rpc-foundation/goldens/live-worktree-name-stream.json index 64803c61faf..ac5006123d2 100644 --- a/mobile/rpc-foundation/goldens/live-worktree-name-stream.json +++ b/mobile/rpc-foundation/goldens/live-worktree-name-stream.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.create-1.json b/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.create-1.json index 393e1e6a3dd..3b85818d93b 100644 --- a/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.create-1.json +++ b/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.create-1.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.createsupport-1.json b/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.createsupport-1.json index f2932f48104..108850abfb8 100644 --- a/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.createsupport-1.json +++ b/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.createsupport-1.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/matrix-agentsession.structured-launch-agentsession.createsupport-1.json b/mobile/rpc-foundation/goldens/matrix-agentsession.structured-launch-agentsession.createsupport-1.json index f3c1dd31dd4..2279cacba82 100644 --- a/mobile/rpc-foundation/goldens/matrix-agentsession.structured-launch-agentsession.createsupport-1.json +++ b/mobile/rpc-foundation/goldens/matrix-agentsession.structured-launch-agentsession.createsupport-1.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.history-aivault.listsessions-1.json b/mobile/rpc-foundation/goldens/matrix-aivault.history-aivault.listsessions-1.json index 9dd00581b43..12a459c5145 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.history-aivault.listsessions-1.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.history-aivault.listsessions-1.json @@ -3,7 +3,7 @@ "family": "aiVault.history", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7add46922ba5486e56c8acd99a53d083b605f5d98f3cc44ee3cb350ec0406080", @@ -86,18 +86,6 @@ } } }, - "1277d47c0e64": { - "activeWorktreePath": "/repo/feature", - "hostStatusResult": { - "$rpc": "null" - }, - "refreshing": false, - "scope": "workspace", - "screenState": { - "kind": "error", - "message": "Cannot read properties of undefined (reading 'sessions')" - } - }, "23523c413cbd": { "activeWorktreePath": "/repo/feature", "hostStatusResult": { @@ -297,18 +285,6 @@ } } }, - "61f76365e23c": { - "activeWorktreePath": "/repo/feature", - "hostStatusResult": { - "$rpc": "null" - }, - "refreshing": false, - "scope": "workspace", - "screenState": { - "kind": "error", - "message": "Cannot read properties of null (reading 'sessions')" - } - }, "698f848e6967": { "activeWorktreePath": "/repo/feature", "hostStatusResult": { @@ -571,21 +547,16 @@ "$rpc": "undefined" } }, - "fc659419e768": { + "fead2d57ab18": { "activeWorktreePath": "/repo/feature", "hostStatusResult": { - "capabilities": ["aiVault.v1"] + "$rpc": "null" }, "refreshing": false, "scope": "workspace", "screenState": { - "issues": { - "$rpc": "undefined" - }, - "kind": "ready", - "sessions": { - "$rpc": "undefined" - } + "kind": "error", + "message": "The host sent a reply this app could not read (aiVault.listSessions)" } } }, @@ -612,7 +583,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "1277d47c0e64", + "state": "fead2d57ab18", "effects": [] } }, @@ -624,7 +595,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "61f76365e23c", + "state": "fead2d57ab18", "effects": [] } }, @@ -636,7 +607,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "fc659419e768", + "state": "fead2d57ab18", "effects": [] } }, @@ -648,7 +619,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "fc659419e768", + "state": "fead2d57ab18", "effects": [] } }, @@ -660,7 +631,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "fc659419e768", + "state": "fead2d57ab18", "effects": [] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-platform-status.json b/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-platform-status.json index df236c12865..a6779db6004 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-platform-status.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-platform-status.json @@ -3,7 +3,7 @@ "family": "aiVault.history-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c78ab47eea594b7e1988403321ff9ba135ca60c513bb9d5848bdde26c0ffe3c3", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-status.get-2.json b/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-status.get-2.json index dc1e62e3ae1..30d685dab7b 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-status.get-2.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-status.get-2.json @@ -3,7 +3,7 @@ "family": "aiVault.history-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c78ab47eea594b7e1988403321ff9ba135ca60c513bb9d5848bdde26c0ffe3c3", @@ -371,27 +371,6 @@ } } }, - "5368af075169": { - "crash": { - "$rpc": "null" - }, - "elements": { - "ChevronLeft": 1, - "Pressable": 3, - "RefreshCw": 1, - "SafeAreaView": 1, - "Text": 5, - "View": 4 - }, - "labels": ["Back", "Refresh agent sessions"], - "text": [ - "Agent Session History", - "orca-history", - "Unable to Load", - "Cannot read properties of undefined (reading 'capabilities')", - "Retry" - ] - }, "6eacae1aa018": { "name": "status.get#2", "ordinal": 5, @@ -518,6 +497,27 @@ } } }, + "8cc4f2d607a6": { + "crash": { + "$rpc": "null" + }, + "elements": { + "ChevronLeft": 1, + "Pressable": 3, + "RefreshCw": 1, + "SafeAreaView": 1, + "Text": 5, + "View": 4 + }, + "labels": ["Back", "Refresh agent sessions"], + "text": [ + "Agent Session History", + "orca-history", + "Unable to Load", + "The host sent a reply this app could not read (status.get)", + "Retry" + ] + }, "8d930b47bf2f": { "name": "worktree.ps#1", "ordinal": 1, @@ -652,27 +652,6 @@ "Retry" ] }, - "c767ef059f7e": { - "crash": { - "$rpc": "null" - }, - "elements": { - "ChevronLeft": 1, - "Pressable": 3, - "RefreshCw": 1, - "SafeAreaView": 1, - "Text": 5, - "View": 4 - }, - "labels": ["Back", "Refresh agent sessions"], - "text": [ - "Agent Session History", - "orca-history", - "Unable to Load", - "Cannot read properties of null (reading 'capabilities')", - "Retry" - ] - }, "cee4e3edb0a1": { "name": "status.get#2", "ordinal": 6, @@ -775,7 +754,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "5368af075169", + "state": "8cc4f2d607a6", "effects": [] } }, @@ -787,7 +766,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "c767ef059f7e", + "state": "8cc4f2d607a6", "effects": [] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-worktree.ps-1.json b/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-worktree.ps-1.json index 8346115e3c3..eb9d19065e7 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-worktree.ps-1.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-worktree.ps-1.json @@ -3,7 +3,7 @@ "family": "aiVault.history-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c78ab47eea594b7e1988403321ff9ba135ca60c513bb9d5848bdde26c0ffe3c3", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.history-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-aivault.history-status.get-1.json index 64cf096b31e..f4e7407b43e 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.history-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.history-status.get-1.json @@ -3,7 +3,7 @@ "family": "aiVault.history", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7add46922ba5486e56c8acd99a53d083b605f5d98f3cc44ee3cb350ec0406080", @@ -274,18 +274,6 @@ } } }, - "4af7915fce72": { - "activeWorktreePath": "/repo/feature", - "hostStatusResult": { - "$rpc": "null" - }, - "refreshing": false, - "scope": "workspace", - "screenState": { - "kind": "error", - "message": "Cannot read properties of undefined (reading 'capabilities')" - } - }, "698f848e6967": { "activeWorktreePath": "/repo/feature", "hostStatusResult": { @@ -429,18 +417,6 @@ "kind": "unsupported" } }, - "b35d53bd952d": { - "activeWorktreePath": "/repo/feature", - "hostStatusResult": { - "$rpc": "null" - }, - "refreshing": false, - "scope": "workspace", - "screenState": { - "kind": "error", - "message": "Cannot read properties of null (reading 'capabilities')" - } - }, "b799ba7b0c33": { "name": "status.get#1", "ordinal": 1, @@ -576,6 +552,18 @@ "$rpc": "undefined" } }, + "f79de442ffa2": { + "activeWorktreePath": "/repo/feature", + "hostStatusResult": { + "$rpc": "null" + }, + "refreshing": false, + "scope": "workspace", + "screenState": { + "kind": "error", + "message": "The host sent a reply this app could not read (status.get)" + } + }, "fd9ea98fae8e": { "activeWorktreePath": "/repo/feature", "hostStatusResult": { @@ -612,7 +600,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "4af7915fce72", + "state": "f79de442ffa2", "effects": [] } }, @@ -624,7 +612,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "b35d53bd952d", + "state": "f79de442ffa2", "effects": [] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-session.tabs.createterminal-1.json b/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-session.tabs.createterminal-1.json index b71411a9a99..2d72bf6a740 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-session.tabs.createterminal-1.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-session.tabs.createterminal-1.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-terminal.send-1.json index a3c383d6260..1df683e2c0e 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.resume-preparation-aivault.preparesessionresume-1.json b/mobile/rpc-foundation/goldens/matrix-aivault.resume-preparation-aivault.preparesessionresume-1.json index 376b39076d1..92d31138743 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.resume-preparation-aivault.preparesessionresume-1.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.resume-preparation-aivault.preparesessionresume-1.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.dialog-browser.dialogaccept-1.json b/mobile/rpc-foundation/goldens/matrix-browser.dialog-browser.dialogaccept-1.json index 0474daaeff7..535d1f208bc 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.dialog-browser.dialogaccept-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.dialog-browser.dialogaccept-1.json @@ -3,7 +3,7 @@ "family": "browser.dialog", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keyboardinserttext-1.json b/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keyboardinserttext-1.json index 52319902d66..cb3be08420c 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keyboardinserttext-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keyboardinserttext-1.json @@ -3,7 +3,7 @@ "family": "browser.keyboard", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keypress-1.json b/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keypress-1.json index c734f497be5..b7674cc2b41 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keypress-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keypress-1.json @@ -3,7 +3,7 @@ "family": "browser.keyboard", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseclick-1.json b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseclick-1.json index a5d6b62036a..e6b80a491fa 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseclick-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseclick-1.json @@ -3,7 +3,7 @@ "family": "browser.pointer-click", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousedown-1.json b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousedown-1.json index 4187b00765a..2c4157c7088 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousedown-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousedown-1.json @@ -3,7 +3,7 @@ "family": "browser.pointer-click", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousemove-1.json b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousemove-1.json index 5c2e15369e1..0ea7fcd6039 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousemove-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousemove-1.json @@ -3,7 +3,7 @@ "family": "browser.pointer-click", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseup-1.json b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseup-1.json index d2c1a83f73c..5dbdd5db6b0 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseup-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseup-1.json @@ -3,7 +3,7 @@ "family": "browser.pointer-click", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousemove-1.json b/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousemove-1.json index cab0bc52dfd..597e94ca599 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousemove-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousemove-1.json @@ -3,7 +3,7 @@ "family": "browser.wheel", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousewheel-1.json b/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousewheel-1.json index 0b568b0333f..4fb0e89f803 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousewheel-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousewheel-1.json @@ -3,7 +3,7 @@ "family": "browser.wheel", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-clipboard.image-attachment-clipboard.startimageupload-1.json b/mobile/rpc-foundation/goldens/matrix-clipboard.image-attachment-clipboard.startimageupload-1.json index e645eb883d8..6cb0ef464b4 100644 --- a/mobile/rpc-foundation/goldens/matrix-clipboard.image-attachment-clipboard.startimageupload-1.json +++ b/mobile/rpc-foundation/goldens/matrix-clipboard.image-attachment-clipboard.startimageupload-1.json @@ -3,7 +3,7 @@ "family": "clipboard.image-attachment", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.saveimageastempfile-1.json b/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.saveimageastempfile-1.json index c88db1965ed..d7236f70362 100644 --- a/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.saveimageastempfile-1.json +++ b/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.saveimageastempfile-1.json @@ -3,7 +3,7 @@ "family": "clipboard.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.startimageupload-1.json b/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.startimageupload-1.json index 81e9c3b9f9e..ae64b7f5008 100644 --- a/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.startimageupload-1.json +++ b/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.startimageupload-1.json @@ -3,7 +3,7 @@ "family": "clipboard.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/matrix-components.codex-reset-capability-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-components.codex-reset-capability-status.get-1.json index e7a96457e17..1c80c13ef55 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.codex-reset-capability-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.codex-reset-capability-status.get-1.json @@ -3,7 +3,7 @@ "family": "components.codex-reset-capability", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/matrix-components.codex-reset-credit-accounts.consumecodexresetcredit-1.json b/mobile/rpc-foundation/goldens/matrix-components.codex-reset-credit-accounts.consumecodexresetcredit-1.json index a8587ad46ef..6a21aa1d2aa 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.codex-reset-credit-accounts.consumecodexresetcredit-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.codex-reset-credit-accounts.consumecodexresetcredit-1.json @@ -3,7 +3,7 @@ "family": "components.codex-reset-credit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "76b53dea504a688493843e23e8e7d052fc196f559bb1e2579d0fd38bf3156d0e", diff --git a/mobile/rpc-foundation/goldens/matrix-components.execution-target-local-preflight.detectagents-1.json b/mobile/rpc-foundation/goldens/matrix-components.execution-target-local-preflight.detectagents-1.json index 02f15783954..f1d2016855e 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.execution-target-local-preflight.detectagents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.execution-target-local-preflight.detectagents-1.json @@ -3,7 +3,7 @@ "family": "components.execution-target-local", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/matrix-components.execution-target-preflight.detectremoteagents-1.json b/mobile/rpc-foundation/goldens/matrix-components.execution-target-preflight.detectremoteagents-1.json index b0e1700b6a0..0505d3630c9 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.execution-target-preflight.detectremoteagents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.execution-target-preflight.detectremoteagents-1.json @@ -3,7 +3,7 @@ "family": "components.execution-target", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.connect-1.json b/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.connect-1.json index cbd0ed8fa86..84cceeb1e0c 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.connect-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.connect-1.json @@ -3,7 +3,7 @@ "family": "components.execution-target", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.getstate-1.json b/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.getstate-1.json index 65fd3e7e31a..6c03eea2d46 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.getstate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.getstate-1.json @@ -3,7 +3,7 @@ "family": "components.execution-target", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/matrix-components.new-workspace-repositories-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-components.new-workspace-repositories-repo.list-1.json index 0fdba3fbe0c..d36c2a9e7a4 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.new-workspace-repositories-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.new-workspace-repositories-repo.list-1.json @@ -3,7 +3,7 @@ "family": "components.new-workspace-repositories", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "64c1772f0f95a3c43fbb14398a8804b2b4784f7f18874e4fd79767ae634c7faa", diff --git a/mobile/rpc-foundation/goldens/matrix-components.setup-script-repo.hooks-1.json b/mobile/rpc-foundation/goldens/matrix-components.setup-script-repo.hooks-1.json index 2ceb8f8aa26..e3062cb7b08 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.setup-script-repo.hooks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.setup-script-repo.hooks-1.json @@ -3,7 +3,7 @@ "family": "components.setup-script", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.list-1.json b/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.list-1.json index 7295e403898..31dca92626b 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.list-1.json @@ -3,7 +3,7 @@ "family": "files.explorer-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7a42a348f4407b94cf75ac77a4b6b783b7d28103b1ae1d111d2708dab0dd4a0c", @@ -417,26 +417,6 @@ "ordinal": 2, "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"files.readDir\",\"params\":{\"worktree\":\"id:wt-files\",\"relativePath\":\"\"}}" }, - "8966ebfaf515": { - "crash": { - "$rpc": "null" - }, - "elements": { - "ChevronLeft": 1, - "Pressable": 2, - "SafeAreaView": 1, - "Text": 4, - "View": 4 - }, - "labels": ["Back to session"], - "rows": [], - "text": [ - "Files", - "orca-files", - "Cannot read properties of undefined (reading 'files')", - "Retry" - ] - }, "a0139a06ef98": { "crash": { "$rpc": "null" @@ -452,36 +432,6 @@ "rows": [], "text": ["Files", "orca-files", "No files found"] }, - "aa33782c6235": { - "crash": { - "$rpc": "null" - }, - "elements": { - "ChevronLeft": 1, - "Pressable": 2, - "SafeAreaView": 1, - "Text": 4, - "View": 4 - }, - "labels": ["Back to session"], - "rows": [], - "text": ["Files", "orca-files", "Cannot read properties of null (reading 'files')", "Retry"] - }, - "acc0ab029cee": { - "crash": { - "$rpc": "null" - }, - "elements": { - "ChevronLeft": 1, - "Pressable": 2, - "SafeAreaView": 1, - "Text": 4, - "View": 4 - }, - "labels": ["Back to session"], - "rows": [], - "text": ["Files", "orca-files", "files is not iterable", "Retry"] - }, "b85511fb8929": { "crash": { "$rpc": "null" @@ -591,6 +541,26 @@ "$rpc": "undefined" } }, + "eededbc093fb": { + "crash": { + "$rpc": "null" + }, + "elements": { + "ChevronLeft": 1, + "Pressable": 2, + "SafeAreaView": 1, + "Text": 4, + "View": 4 + }, + "labels": ["Back to session"], + "rows": [], + "text": [ + "Files", + "orca-files", + "The host sent a reply this app could not read (files.list)", + "Retry" + ] + }, "f9892850cc0f": { "name": "files.list#1", "ordinal": 3, @@ -659,7 +629,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "8966ebfaf515", + "state": "eededbc093fb", "effects": [] } }, @@ -671,7 +641,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "aa33782c6235", + "state": "eededbc093fb", "effects": [] } }, @@ -683,7 +653,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "acc0ab029cee", + "state": "eededbc093fb", "effects": [] } }, @@ -695,7 +665,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "acc0ab029cee", + "state": "eededbc093fb", "effects": [] } }, @@ -707,7 +677,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "acc0ab029cee", + "state": "eededbc093fb", "effects": [] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.readdir-1.json b/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.readdir-1.json index 63cc8bd0f7c..faa67607c2d 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.readdir-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.readdir-1.json @@ -3,7 +3,7 @@ "family": "files.explorer-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7a42a348f4407b94cf75ac77a4b6b783b7d28103b1ae1d111d2708dab0dd4a0c", @@ -359,6 +359,26 @@ } } }, + "6c25da20f340": { + "crash": { + "$rpc": "null" + }, + "elements": { + "ChevronLeft": 1, + "Pressable": 2, + "SafeAreaView": 1, + "Text": 4, + "View": 4 + }, + "labels": ["Back to session"], + "rows": [], + "text": [ + "Files", + "orca-files", + "The host sent a reply this app could not read (files.readDir)", + "Retry" + ] + }, "7135933422f5": { "name": "files.readDir#1", "ordinal": 1, @@ -506,13 +526,6 @@ } } }, - "bcba3c565d8e": { - "name": "screen.crash", - "ordinal": 3, - "value": { - "message": "entries.filter is not a function" - } - }, "e0b32411ccef": { "name": "files.readDir#1", "ordinal": 1, @@ -562,13 +575,6 @@ "rows": [], "text": ["Files", "orca-files"] }, - "ea2c96b08e6b": { - "crash": "entries.filter is not a function", - "elements": {}, - "labels": [], - "rows": [], - "text": [] - }, "eb79a9b3682a": { "status": "fulfilled", "startedAt": 0, @@ -655,7 +661,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "a0139a06ef98", + "state": "6c25da20f340", "effects": [] } }, @@ -667,7 +673,7 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "a0139a06ef98", + "state": "6c25da20f340", "effects": [] } }, @@ -679,8 +685,8 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "ea2c96b08e6b", - "effects": ["bcba3c565d8e"] + "state": "6c25da20f340", + "effects": [] } }, { @@ -691,8 +697,8 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "ea2c96b08e6b", - "effects": ["bcba3c565d8e"] + "state": "6c25da20f340", + "effects": [] } }, { @@ -703,8 +709,8 @@ "settlements": { "mount": "eb79a9b3682a" }, - "state": "ea2c96b08e6b", - "effects": ["bcba3c565d8e"] + "state": "6c25da20f340", + "effects": [] } }, { diff --git a/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-ssh.getstate-1.json b/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-ssh.getstate-1.json index 61fea3f8ed5..72618fcd072 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-ssh.getstate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-ssh.getstate-1.json @@ -3,7 +3,7 @@ "family": "files.mutation-ownership", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", @@ -353,6 +353,17 @@ } } }, + "c4c200b5bf69": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (ssh.getState)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, "c7584e82c72f": { "status": "rejected", "startedAt": 0, @@ -400,16 +411,6 @@ "ordinal": 4, "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"worktree.show\",\"params\":{\"worktree\":\"id:workspace-1\"}}" }, - "ce2b29907ae3": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "TypeError", - "message": "Cannot read properties of null (reading 'state')", - "isRpcDeliveryUnknown": false - } - }, "d08f74d65ee6": { "name": "status.get#1", "ordinal": 2, @@ -452,16 +453,6 @@ } } }, - "d954a0a142a5": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "TypeError", - "message": "Cannot read properties of undefined (reading 'state')", - "isRpcDeliveryUnknown": false - } - }, "e6c4b2665e65": { "name": "ssh.getState#1", "ordinal": 5, @@ -644,7 +635,7 @@ "sender": ["e81d3a627ac2", "3cf4416a7928", "0622950ee1c9"], "payloads": ["d08f74d65ee6", "c924e7a5a7da", "088efd8ff3f7"], "settlements": { - "capture": "d954a0a142a5" + "capture": "c4c200b5bf69" }, "state": "518ec57c381a", "effects": [] @@ -656,7 +647,7 @@ "sender": ["e81d3a627ac2", "3cf4416a7928", "7fb21c6984cd"], "payloads": ["d08f74d65ee6", "c924e7a5a7da", "088efd8ff3f7"], "settlements": { - "capture": "ce2b29907ae3" + "capture": "c4c200b5bf69" }, "state": "518ec57c381a", "effects": [] diff --git a/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-status.get-1.json index 640e691bd42..c4a05864640 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-status.get-1.json @@ -3,7 +3,7 @@ "family": "files.mutation-ownership", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-worktree.show-1.json index 7598f9f0ce5..fa6f67a8530 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "files.mutation-ownership", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", @@ -13,6 +13,17 @@ "projectionVersion": 2, "goldenFormatVersion": 5, "values": { + "0233661d0a29": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (worktree.show)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, "088efd8ff3f7": { "name": "ssh.getState#1", "ordinal": 6, @@ -89,16 +100,6 @@ } } }, - "2588fd63a157": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "TypeError", - "message": "Cannot read properties of undefined (reading 'worktree')", - "isRpcDeliveryUnknown": false - } - }, "29bfbe94cca9": { "status": "fulfilled", "startedAt": 0, @@ -443,16 +444,6 @@ "isRpcDeliveryUnknown": true } }, - "b5447f4dd931": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "TypeError", - "message": "Cannot read properties of null (reading 'worktree')", - "isRpcDeliveryUnknown": false - } - }, "b6e8ae2b152e": { "name": "ssh.getState#1", "ordinal": 5, @@ -644,7 +635,7 @@ "sender": ["e81d3a627ac2", "2edf2dc7f524"], "payloads": ["d08f74d65ee6", "c924e7a5a7da"], "settlements": { - "capture": "2588fd63a157" + "capture": "0233661d0a29" }, "state": "518ec57c381a", "effects": [] @@ -656,7 +647,7 @@ "sender": ["e81d3a627ac2", "c8d8aec2ecbb"], "payloads": ["d08f74d65ee6", "c924e7a5a7da"], "settlements": { - "capture": "b5447f4dd931" + "capture": "0233661d0a29" }, "state": "518ec57c381a", "effects": [] diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-artifact-image-files.readterminalartifactpreview-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-artifact-image-files.readterminalartifactpreview-1.json new file mode 100644 index 00000000000..70c6c8afe0c --- /dev/null +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-artifact-image-files.readterminalartifactpreview-1.json @@ -0,0 +1,643 @@ +{ + "operation": "files.preview-load", + "family": "files.preview-artifact-image", + "namedDeltas": [], + "runnerVersion": 1, + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", + "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", + "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", + "scenarioSha256": "83cad9595793b30d38115ba7118ae1400ecb2af21327853183e91289f4f33a04", + "platform": "darwin", + "scenarioVersion": 1, + "projectionVersion": 2, + "goldenFormatVersion": 5, + "values": { + "011a73e13887": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readTerminalArtifactPreview" + }, + { + "name": "params", + "value": { + "absolutePath": "/logs/shot.png", + "grantId": "grant-1", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "$rpc": "null" + } + } + } + }, + "0254bd812679": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readTerminalArtifactPreview" + }, + { + "name": "params", + "value": { + "absolutePath": "/logs/shot.png", + "grantId": "grant-1", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "transport failure", + "isRpcDeliveryUnknown": true + } + } + }, + "0639efe16769": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "message": "Binary preview unavailable", + "reconnect": false, + "status": "error" + } + }, + "06ce305bc756": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readTerminalArtifactPreview" + }, + { + "name": "params", + "value": { + "absolutePath": "/logs/shot.png", + "grantId": "grant-1", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "error": "refused" + } + } + } + }, + "15467bba2d60": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "message": "Unable to load preview", + "reconnect": false, + "status": "error" + } + }, + "491897d7dd5d": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readTerminalArtifactPreview" + }, + { + "name": "params", + "value": { + "absolutePath": "/logs/shot.png", + "grantId": "grant-1", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "method_not_found", + "message": "Unknown method" + }, + "id": "frame-1", + "ok": false + } + } + }, + "645c5754be42": { + "preview": "unloaded" + }, + "6c3ad19b3a5a": { + "preview": { + "message": "Binary preview unavailable", + "reconnect": false, + "status": "error" + } + }, + "75ebb08fd335": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readTerminalArtifactPreview" + }, + { + "name": "params", + "value": { + "absolutePath": "/logs/shot.png", + "grantId": "grant-1", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "refused", + "message": "outer refused" + }, + "id": "frame-1", + "ok": false + } + } + }, + "7659b8b575da": { + "preview": { + "dataUri": "data:image/png;base64,aGk=", + "kind": "image", + "status": "ready" + } + }, + "879996a2a9e7": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readTerminalArtifactPreview" + }, + { + "name": "params", + "value": { + "absolutePath": "/logs/shot.png", + "grantId": "grant-1", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "", + "isRpcDeliveryUnknown": true + } + } + }, + "a947768bc0ed": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "transport failure", + "isRpcDeliveryUnknown": true + } + }, + "b5608d3d00c8": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 2, + "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"files.readTerminalArtifactPreview\",\"params\":{\"worktree\":\"id:workspace-1\",\"absolutePath\":\"/logs/shot.png\",\"grantId\":\"grant-1\"}}" + }, + "b96f3d16bdfc": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readTerminalArtifactPreview" + }, + { + "name": "params", + "value": { + "absolutePath": "/logs/shot.png", + "grantId": "grant-1", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "error": { + "message": "inner refused" + }, + "ok": false + } + } + } + }, + "bf5d986bfb2b": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readTerminalArtifactPreview" + }, + { + "name": "params", + "value": { + "absolutePath": "/logs/shot.png", + "grantId": "grant-1", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "error": "inner refused", + "ok": false + } + } + } + }, + "c7584e82c72f": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "", + "isRpcDeliveryUnknown": true + } + }, + "ce511c0ab8ac": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readTerminalArtifactPreview" + }, + { + "name": "params", + "value": { + "absolutePath": "/logs/shot.png", + "grantId": "grant-1", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "content": "aGk=", + "isBinary": true, + "isImage": true, + "mimeType": "image/png" + } + } + } + }, + "d3fa8c5b05e4": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readTerminalArtifactPreview" + }, + { + "name": "params", + "value": { + "absolutePath": "/logs/shot.png", + "grantId": "grant-1", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "refused", + "message": "" + }, + "id": "frame-1", + "ok": false + } + } + }, + "dd9af848f659": { + "name": "files.readTerminalArtifactPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readTerminalArtifactPreview" + }, + { + "name": "params", + "value": { + "absolutePath": "/logs/shot.png", + "grantId": "grant-1", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true + } + } + }, + "eed3c09414c4": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (files.readTerminalArtifactPreview)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, + "eee847a9d90d": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "dataUri": "data:image/png;base64,aGk=", + "kind": "image", + "status": "ready" + } + }, + "fba5e3c89244": { + "preview": { + "message": "Unable to load preview", + "reconnect": false, + "status": "error" + } + } + }, + "recording": { + "scenario": "matrix-files.preview-artifact-image-files.readterminalartifactpreview-1", + "checkpoints": [ + { + "id": "files-preview-artifact-image-read.normal:settled", + "observation": { + "sender": ["ce511c0ab8ac"], + "payloads": ["b5608d3d00c8"], + "settlements": { + "load": "eee847a9d90d" + }, + "state": "7659b8b575da", + "effects": [] + } + }, + { + "id": "files-preview-artifact-image-read.result-absent:settled", + "observation": { + "sender": ["dd9af848f659"], + "payloads": ["b5608d3d00c8"], + "settlements": { + "load": "eed3c09414c4" + }, + "state": "645c5754be42", + "effects": [] + } + }, + { + "id": "files-preview-artifact-image-read.result-null:settled", + "observation": { + "sender": ["011a73e13887"], + "payloads": ["b5608d3d00c8"], + "settlements": { + "load": "eed3c09414c4" + }, + "state": "645c5754be42", + "effects": [] + } + }, + { + "id": "files-preview-artifact-image-read.inner-ok-missing:settled", + "observation": { + "sender": ["06ce305bc756"], + "payloads": ["b5608d3d00c8"], + "settlements": { + "load": "0639efe16769" + }, + "state": "6c3ad19b3a5a", + "effects": [] + } + }, + { + "id": "files-preview-artifact-image-read.inner-false-string-error:settled", + "observation": { + "sender": ["bf5d986bfb2b"], + "payloads": ["b5608d3d00c8"], + "settlements": { + "load": "0639efe16769" + }, + "state": "6c3ad19b3a5a", + "effects": [] + } + }, + { + "id": "files-preview-artifact-image-read.inner-false-object-error:settled", + "observation": { + "sender": ["b96f3d16bdfc"], + "payloads": ["b5608d3d00c8"], + "settlements": { + "load": "0639efe16769" + }, + "state": "6c3ad19b3a5a", + "effects": [] + } + }, + { + "id": "files-preview-artifact-image-read.outer-refused:settled", + "observation": { + "sender": ["75ebb08fd335"], + "payloads": ["b5608d3d00c8"], + "settlements": { + "load": "15467bba2d60" + }, + "state": "fba5e3c89244", + "effects": [] + } + }, + { + "id": "files-preview-artifact-image-read.outer-refused-no-message:settled", + "observation": { + "sender": ["d3fa8c5b05e4"], + "payloads": ["b5608d3d00c8"], + "settlements": { + "load": "15467bba2d60" + }, + "state": "fba5e3c89244", + "effects": [] + } + }, + { + "id": "files-preview-artifact-image-read.method-not-found:settled", + "observation": { + "sender": ["491897d7dd5d"], + "payloads": ["b5608d3d00c8"], + "settlements": { + "load": "15467bba2d60" + }, + "state": "fba5e3c89244", + "effects": [] + } + }, + { + "id": "files-preview-artifact-image-read.transport-rejection:settled", + "observation": { + "sender": ["0254bd812679"], + "payloads": ["b5608d3d00c8"], + "settlements": { + "load": "a947768bc0ed" + }, + "state": "645c5754be42", + "effects": [] + } + }, + { + "id": "files-preview-artifact-image-read.transport-rejection-no-message:settled", + "observation": { + "sender": ["879996a2a9e7"], + "payloads": ["b5608d3d00c8"], + "settlements": { + "load": "c7584e82c72f" + }, + "state": "645c5754be42", + "effects": [] + } + } + ] + } +} diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-1.json index 74d5ad6eecf..ca399e3363f 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-1.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", @@ -62,6 +62,17 @@ "status": "error" } }, + "2198677d4b7f": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (files.readTerminalArtifact)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, "2cbe3d82fe7d": { "name": "files.readTerminalArtifact#1", "ordinal": 1, @@ -543,9 +554,9 @@ "sender": ["359261481665"], "payloads": ["5beb3ae90517"], "settlements": { - "load": "15467bba2d60" + "load": "2198677d4b7f" }, - "state": "fba5e3c89244", + "state": "645c5754be42", "effects": [] } }, @@ -555,9 +566,9 @@ "sender": ["fc8164cc9095"], "payloads": ["5beb3ae90517"], "settlements": { - "load": "15467bba2d60" + "load": "2198677d4b7f" }, - "state": "fba5e3c89244", + "state": "645c5754be42", "effects": [] } }, @@ -567,9 +578,9 @@ "sender": ["6eb653749642"], "payloads": ["5beb3ae90517"], "settlements": { - "load": "15467bba2d60" + "load": "2198677d4b7f" }, - "state": "fba5e3c89244", + "state": "645c5754be42", "effects": [] } }, @@ -579,9 +590,9 @@ "sender": ["ce7d56dd9080"], "payloads": ["5beb3ae90517"], "settlements": { - "load": "15467bba2d60" + "load": "2198677d4b7f" }, - "state": "fba5e3c89244", + "state": "645c5754be42", "effects": [] } }, @@ -591,9 +602,9 @@ "sender": ["0181c916a00b"], "payloads": ["5beb3ae90517"], "settlements": { - "load": "15467bba2d60" + "load": "2198677d4b7f" }, - "state": "fba5e3c89244", + "state": "645c5754be42", "effects": [] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-2.json b/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-2.json index 4bc240f0d77..cbe16c69333 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-2.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-2.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", @@ -60,6 +60,17 @@ "status": "error" } }, + "2198677d4b7f": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (files.readTerminalArtifact)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, "29e625f367a7": { "name": "files.resolveTerminalPath#1", "ordinal": 3, @@ -646,9 +657,9 @@ "sender": ["0ea42bf424ad", "29e625f367a7", "bdf8e4e0083f"], "payloads": ["5beb3ae90517", "3fa46af4bb15", "679de887534f"], "settlements": { - "load": "15467bba2d60" + "load": "2198677d4b7f" }, - "state": "fba5e3c89244", + "state": "645c5754be42", "effects": ["ddb7adf79b98"] } }, @@ -658,9 +669,9 @@ "sender": ["0ea42bf424ad", "29e625f367a7", "842035a85a1c"], "payloads": ["5beb3ae90517", "3fa46af4bb15", "679de887534f"], "settlements": { - "load": "15467bba2d60" + "load": "2198677d4b7f" }, - "state": "fba5e3c89244", + "state": "645c5754be42", "effects": ["ddb7adf79b98"] } }, @@ -670,9 +681,9 @@ "sender": ["0ea42bf424ad", "29e625f367a7", "f24fc15f029f"], "payloads": ["5beb3ae90517", "3fa46af4bb15", "679de887534f"], "settlements": { - "load": "15467bba2d60" + "load": "2198677d4b7f" }, - "state": "fba5e3c89244", + "state": "645c5754be42", "effects": ["ddb7adf79b98"] } }, @@ -682,9 +693,9 @@ "sender": ["0ea42bf424ad", "29e625f367a7", "dc6e0288b015"], "payloads": ["5beb3ae90517", "3fa46af4bb15", "679de887534f"], "settlements": { - "load": "15467bba2d60" + "load": "2198677d4b7f" }, - "state": "fba5e3c89244", + "state": "645c5754be42", "effects": ["ddb7adf79b98"] } }, @@ -694,9 +705,9 @@ "sender": ["0ea42bf424ad", "29e625f367a7", "503d66d8b67a"], "payloads": ["5beb3ae90517", "3fa46af4bb15", "679de887534f"], "settlements": { - "load": "15467bba2d60" + "load": "2198677d4b7f" }, - "state": "fba5e3c89244", + "state": "645c5754be42", "effects": ["ddb7adf79b98"] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.resolveterminalpath-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.resolveterminalpath-1.json index b41ab45fc7e..bf801aaa41e 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.resolveterminalpath-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.resolveterminalpath-1.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", @@ -343,6 +343,17 @@ "truncated": false } }, + "54c8d9c728f2": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (files.resolveTerminalPath)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, "5beb3ae90517": { "name": "files.readTerminalArtifact#1", "ordinal": 2, @@ -656,9 +667,9 @@ "sender": ["0ea42bf424ad", "ad1560697ac8"], "payloads": ["5beb3ae90517", "3fa46af4bb15"], "settlements": { - "load": "15467bba2d60" + "load": "54c8d9c728f2" }, - "state": "fba5e3c89244", + "state": "645c5754be42", "effects": [] } }, @@ -668,9 +679,9 @@ "sender": ["0ea42bf424ad", "09ce12d7b4ef"], "payloads": ["5beb3ae90517", "3fa46af4bb15"], "settlements": { - "load": "15467bba2d60" + "load": "54c8d9c728f2" }, - "state": "fba5e3c89244", + "state": "645c5754be42", "effects": [] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.readterminalartifact-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.readterminalartifact-1.json index 56a83159526..7c80759ea10 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.readterminalartifact-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.readterminalartifact-1.json @@ -3,7 +3,7 @@ "family": "files.preview-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", @@ -62,6 +62,17 @@ "status": "error" } }, + "2198677d4b7f": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (files.readTerminalArtifact)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, "24ffb7059792": { "name": "files.readTerminalArtifact#1", "ordinal": 1, @@ -577,9 +588,9 @@ "sender": ["359261481665"], "payloads": ["5beb3ae90517"], "settlements": { - "save": "15467bba2d60" + "save": "2198677d4b7f" }, - "state": "5ac141a87b5a", + "state": "935100df69e4", "effects": [] } }, @@ -589,9 +600,9 @@ "sender": ["fc8164cc9095"], "payloads": ["5beb3ae90517"], "settlements": { - "save": "15467bba2d60" + "save": "2198677d4b7f" }, - "state": "5ac141a87b5a", + "state": "935100df69e4", "effects": [] } }, @@ -601,9 +612,9 @@ "sender": ["6eb653749642"], "payloads": ["5beb3ae90517"], "settlements": { - "save": "15467bba2d60" + "save": "2198677d4b7f" }, - "state": "5ac141a87b5a", + "state": "935100df69e4", "effects": [] } }, @@ -613,9 +624,9 @@ "sender": ["ce7d56dd9080"], "payloads": ["5beb3ae90517"], "settlements": { - "save": "15467bba2d60" + "save": "2198677d4b7f" }, - "state": "5ac141a87b5a", + "state": "935100df69e4", "effects": [] } }, @@ -625,9 +636,9 @@ "sender": ["0181c916a00b"], "payloads": ["5beb3ae90517"], "settlements": { - "save": "15467bba2d60" + "save": "2198677d4b7f" }, - "state": "5ac141a87b5a", + "state": "935100df69e4", "effects": [] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.writeterminalartifact-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.writeterminalartifact-1.json index dde3b94fc13..3da5920cac5 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.writeterminalartifact-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.writeterminalartifact-1.json @@ -3,7 +3,7 @@ "family": "files.preview-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-image-files.readpreview-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-image-files.readpreview-1.json new file mode 100644 index 00000000000..651514e738c --- /dev/null +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-image-files.readpreview-1.json @@ -0,0 +1,632 @@ +{ + "operation": "files.preview-load", + "family": "files.preview-worktree-image", + "namedDeltas": [], + "runnerVersion": 1, + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", + "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", + "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", + "scenarioSha256": "0962cb776492001a60db2ca89d715faec94c648962f140077fc9a85355784d81", + "platform": "darwin", + "scenarioVersion": 1, + "projectionVersion": 2, + "goldenFormatVersion": 5, + "values": { + "0639efe16769": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "message": "Binary preview unavailable", + "reconnect": false, + "status": "error" + } + }, + "15467bba2d60": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "message": "Unable to load preview", + "reconnect": false, + "status": "error" + } + }, + "211cdb1558da": { + "name": "files.readPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readPreview" + }, + { + "name": "params", + "value": { + "relativePath": "docs/logo.png", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "refused", + "message": "outer refused" + }, + "id": "frame-1", + "ok": false + } + } + }, + "54071f66c6d8": { + "name": "files.readPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readPreview" + }, + { + "name": "params", + "value": { + "relativePath": "docs/logo.png", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "refused", + "message": "" + }, + "id": "frame-1", + "ok": false + } + } + }, + "56177906925a": { + "name": "files.readPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readPreview" + }, + { + "name": "params", + "value": { + "relativePath": "docs/logo.png", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "error": "refused" + } + } + } + }, + "5c71a0c5523d": { + "name": "files.readPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readPreview" + }, + { + "name": "params", + "value": { + "relativePath": "docs/logo.png", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true + } + } + }, + "645c5754be42": { + "preview": "unloaded" + }, + "67dbd4c82715": { + "name": "files.readPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readPreview" + }, + { + "name": "params", + "value": { + "relativePath": "docs/logo.png", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "method_not_found", + "message": "Unknown method" + }, + "id": "frame-1", + "ok": false + } + } + }, + "6af2188434b2": { + "name": "files.readPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readPreview" + }, + { + "name": "params", + "value": { + "relativePath": "docs/logo.png", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "transport failure", + "isRpcDeliveryUnknown": true + } + } + }, + "6c3ad19b3a5a": { + "preview": { + "message": "Binary preview unavailable", + "reconnect": false, + "status": "error" + } + }, + "70f782fa6ef2": { + "name": "files.readPreview#1", + "ordinal": 2, + "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"files.readPreview\",\"params\":{\"worktree\":\"id:workspace-1\",\"relativePath\":\"docs/logo.png\"}}" + }, + "7659b8b575da": { + "preview": { + "dataUri": "data:image/png;base64,aGk=", + "kind": "image", + "status": "ready" + } + }, + "79a49cf80ecf": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (files.readPreview)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, + "a51f35ec407c": { + "name": "files.readPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readPreview" + }, + { + "name": "params", + "value": { + "relativePath": "docs/logo.png", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "error": { + "message": "inner refused" + }, + "ok": false + } + } + } + }, + "a568af21f540": { + "name": "files.readPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readPreview" + }, + { + "name": "params", + "value": { + "relativePath": "docs/logo.png", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "content": "aGk=", + "isBinary": true, + "isImage": true, + "mimeType": "image/png" + } + } + } + }, + "a947768bc0ed": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "transport failure", + "isRpcDeliveryUnknown": true + } + }, + "af09828e5413": { + "name": "files.readPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readPreview" + }, + { + "name": "params", + "value": { + "relativePath": "docs/logo.png", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "$rpc": "null" + } + } + } + }, + "c4730e0ddd60": { + "name": "files.readPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readPreview" + }, + { + "name": "params", + "value": { + "relativePath": "docs/logo.png", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "error": "inner refused", + "ok": false + } + } + } + }, + "c7584e82c72f": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "", + "isRpcDeliveryUnknown": true + } + }, + "ee977c2d3628": { + "name": "files.readPreview#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.readPreview" + }, + { + "name": "params", + "value": { + "relativePath": "docs/logo.png", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "", + "isRpcDeliveryUnknown": true + } + } + }, + "eee847a9d90d": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "dataUri": "data:image/png;base64,aGk=", + "kind": "image", + "status": "ready" + } + }, + "fba5e3c89244": { + "preview": { + "message": "Unable to load preview", + "reconnect": false, + "status": "error" + } + } + }, + "recording": { + "scenario": "matrix-files.preview-worktree-image-files.readpreview-1", + "checkpoints": [ + { + "id": "files-preview-worktree-image-read.normal:settled", + "observation": { + "sender": ["a568af21f540"], + "payloads": ["70f782fa6ef2"], + "settlements": { + "load": "eee847a9d90d" + }, + "state": "7659b8b575da", + "effects": [] + } + }, + { + "id": "files-preview-worktree-image-read.result-absent:settled", + "observation": { + "sender": ["5c71a0c5523d"], + "payloads": ["70f782fa6ef2"], + "settlements": { + "load": "79a49cf80ecf" + }, + "state": "645c5754be42", + "effects": [] + } + }, + { + "id": "files-preview-worktree-image-read.result-null:settled", + "observation": { + "sender": ["af09828e5413"], + "payloads": ["70f782fa6ef2"], + "settlements": { + "load": "79a49cf80ecf" + }, + "state": "645c5754be42", + "effects": [] + } + }, + { + "id": "files-preview-worktree-image-read.inner-ok-missing:settled", + "observation": { + "sender": ["56177906925a"], + "payloads": ["70f782fa6ef2"], + "settlements": { + "load": "0639efe16769" + }, + "state": "6c3ad19b3a5a", + "effects": [] + } + }, + { + "id": "files-preview-worktree-image-read.inner-false-string-error:settled", + "observation": { + "sender": ["c4730e0ddd60"], + "payloads": ["70f782fa6ef2"], + "settlements": { + "load": "0639efe16769" + }, + "state": "6c3ad19b3a5a", + "effects": [] + } + }, + { + "id": "files-preview-worktree-image-read.inner-false-object-error:settled", + "observation": { + "sender": ["a51f35ec407c"], + "payloads": ["70f782fa6ef2"], + "settlements": { + "load": "0639efe16769" + }, + "state": "6c3ad19b3a5a", + "effects": [] + } + }, + { + "id": "files-preview-worktree-image-read.outer-refused:settled", + "observation": { + "sender": ["211cdb1558da"], + "payloads": ["70f782fa6ef2"], + "settlements": { + "load": "15467bba2d60" + }, + "state": "fba5e3c89244", + "effects": [] + } + }, + { + "id": "files-preview-worktree-image-read.outer-refused-no-message:settled", + "observation": { + "sender": ["54071f66c6d8"], + "payloads": ["70f782fa6ef2"], + "settlements": { + "load": "15467bba2d60" + }, + "state": "fba5e3c89244", + "effects": [] + } + }, + { + "id": "files-preview-worktree-image-read.method-not-found:settled", + "observation": { + "sender": ["67dbd4c82715"], + "payloads": ["70f782fa6ef2"], + "settlements": { + "load": "15467bba2d60" + }, + "state": "fba5e3c89244", + "effects": [] + } + }, + { + "id": "files-preview-worktree-image-read.transport-rejection:settled", + "observation": { + "sender": ["6af2188434b2"], + "payloads": ["70f782fa6ef2"], + "settlements": { + "load": "a947768bc0ed" + }, + "state": "645c5754be42", + "effects": [] + } + }, + { + "id": "files-preview-worktree-image-read.transport-rejection-no-message:settled", + "observation": { + "sender": ["ee977c2d3628"], + "payloads": ["70f782fa6ef2"], + "settlements": { + "load": "c7584e82c72f" + }, + "state": "645c5754be42", + "effects": [] + } + } + ] + } +} diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-text-files.read-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-text-files.read-1.json new file mode 100644 index 00000000000..7afaf630fb4 --- /dev/null +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-text-files.read-1.json @@ -0,0 +1,618 @@ +{ + "operation": "files.preview-load", + "family": "files.preview-worktree-text", + "namedDeltas": [], + "runnerVersion": 1, + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", + "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", + "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", + "scenarioSha256": "08d73805619f00748caa5726a7fb0d9b9c561f93ec1e6dd28b27065a4515d65d", + "platform": "darwin", + "scenarioVersion": 1, + "projectionVersion": 2, + "goldenFormatVersion": 5, + "values": { + "072647254b82": { + "name": "files.read#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "error": { + "message": "inner refused" + }, + "ok": false + } + } + } + }, + "10dde7cd8f0e": { + "name": "files.read#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "$rpc": "null" + } + } + } + }, + "15467bba2d60": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "message": "Unable to load preview", + "reconnect": false, + "status": "error" + } + }, + "1cf4496bc8c0": { + "name": "files.read#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "byteLength": 8, + "content": "# readme", + "truncated": false + } + } + } + }, + "3f8bf3069e3d": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "byteLength": 8, + "content": "# readme", + "kind": "markdown", + "status": "ready", + "truncated": false + } + }, + "43e3786742e9": { + "name": "files.read#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true + } + } + }, + "47ef2e397e18": { + "preview": { + "byteLength": 8, + "content": "# readme", + "kind": "markdown", + "status": "ready", + "truncated": false + } + }, + "645c5754be42": { + "preview": "unloaded" + }, + "786a079aa332": { + "name": "files.read#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "", + "isRpcDeliveryUnknown": true + } + } + }, + "8423ff93fda2": { + "name": "files.read#1", + "ordinal": 2, + "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"files.read\",\"params\":{\"worktree\":\"id:workspace-1\",\"relativePath\":\"docs/readme.md\"}}" + }, + "91f40f652017": { + "name": "files.read#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "refused", + "message": "outer refused" + }, + "id": "frame-1", + "ok": false + } + } + }, + "9def31fe4536": { + "name": "files.read#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "transport failure", + "isRpcDeliveryUnknown": true + } + } + }, + "a947768bc0ed": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "transport failure", + "isRpcDeliveryUnknown": true + } + }, + "bbbd11388ff3": { + "name": "files.read#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "refused", + "message": "" + }, + "id": "frame-1", + "ok": false + } + } + }, + "c7584e82c72f": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "", + "isRpcDeliveryUnknown": true + } + }, + "d7d97e5dac06": { + "name": "files.read#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "error": "inner refused", + "ok": false + } + } + } + }, + "e05b0a48111b": { + "name": "files.read#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "method_not_found", + "message": "Unknown method" + }, + "id": "frame-1", + "ok": false + } + } + }, + "fba5e3c89244": { + "preview": { + "message": "Unable to load preview", + "reconnect": false, + "status": "error" + } + }, + "fcbcdb353528": { + "name": "files.read#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "error": "refused" + } + } + } + }, + "fed544a8befe": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (files.read)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + } + }, + "recording": { + "scenario": "matrix-files.preview-worktree-text-files.read-1", + "checkpoints": [ + { + "id": "files-preview-worktree-text-read.normal:settled", + "observation": { + "sender": ["1cf4496bc8c0"], + "payloads": ["8423ff93fda2"], + "settlements": { + "load": "3f8bf3069e3d" + }, + "state": "47ef2e397e18", + "effects": [] + } + }, + { + "id": "files-preview-worktree-text-read.result-absent:settled", + "observation": { + "sender": ["43e3786742e9"], + "payloads": ["8423ff93fda2"], + "settlements": { + "load": "fed544a8befe" + }, + "state": "645c5754be42", + "effects": [] + } + }, + { + "id": "files-preview-worktree-text-read.result-null:settled", + "observation": { + "sender": ["10dde7cd8f0e"], + "payloads": ["8423ff93fda2"], + "settlements": { + "load": "fed544a8befe" + }, + "state": "645c5754be42", + "effects": [] + } + }, + { + "id": "files-preview-worktree-text-read.inner-ok-missing:settled", + "observation": { + "sender": ["fcbcdb353528"], + "payloads": ["8423ff93fda2"], + "settlements": { + "load": "fed544a8befe" + }, + "state": "645c5754be42", + "effects": [] + } + }, + { + "id": "files-preview-worktree-text-read.inner-false-string-error:settled", + "observation": { + "sender": ["d7d97e5dac06"], + "payloads": ["8423ff93fda2"], + "settlements": { + "load": "fed544a8befe" + }, + "state": "645c5754be42", + "effects": [] + } + }, + { + "id": "files-preview-worktree-text-read.inner-false-object-error:settled", + "observation": { + "sender": ["072647254b82"], + "payloads": ["8423ff93fda2"], + "settlements": { + "load": "fed544a8befe" + }, + "state": "645c5754be42", + "effects": [] + } + }, + { + "id": "files-preview-worktree-text-read.outer-refused:settled", + "observation": { + "sender": ["91f40f652017"], + "payloads": ["8423ff93fda2"], + "settlements": { + "load": "15467bba2d60" + }, + "state": "fba5e3c89244", + "effects": [] + } + }, + { + "id": "files-preview-worktree-text-read.outer-refused-no-message:settled", + "observation": { + "sender": ["bbbd11388ff3"], + "payloads": ["8423ff93fda2"], + "settlements": { + "load": "15467bba2d60" + }, + "state": "fba5e3c89244", + "effects": [] + } + }, + { + "id": "files-preview-worktree-text-read.method-not-found:settled", + "observation": { + "sender": ["e05b0a48111b"], + "payloads": ["8423ff93fda2"], + "settlements": { + "load": "15467bba2d60" + }, + "state": "fba5e3c89244", + "effects": [] + } + }, + { + "id": "files-preview-worktree-text-read.transport-rejection:settled", + "observation": { + "sender": ["9def31fe4536"], + "payloads": ["8423ff93fda2"], + "settlements": { + "load": "a947768bc0ed" + }, + "state": "645c5754be42", + "effects": [] + } + }, + { + "id": "files-preview-worktree-text-read.transport-rejection-no-message:settled", + "observation": { + "sender": ["786a079aa332"], + "payloads": ["8423ff93fda2"], + "settlements": { + "load": "c7584e82c72f" + }, + "state": "645c5754be42", + "effects": [] + } + } + ] + } +} diff --git a/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.read-1.json b/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.read-1.json index b255b694122..0d1ecd509e5 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.read-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.read-1.json @@ -3,7 +3,7 @@ "family": "files.tab-doc", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", @@ -232,24 +232,6 @@ } } }, - "5a33eeedb90f": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "byteLength": { - "$rpc": "undefined" - }, - "content": { - "$rpc": "undefined" - }, - "kind": "file", - "status": "ready", - "truncated": { - "$rpc": "undefined" - } - } - }, "786a079aa332": { "name": "files.read#1", "ordinal": 1, @@ -394,16 +376,6 @@ } } }, - "a7c7f43265d5": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "TypeError", - "message": "Cannot read properties of null (reading 'content')", - "isRpcDeliveryUnknown": false - } - }, "a947768bc0ed": { "status": "rejected", "startedAt": 0, @@ -436,16 +408,6 @@ "isRpcDeliveryUnknown": false } }, - "ba9332ae7bb1": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "TypeError", - "message": "Cannot read properties of undefined (reading 'content')", - "isRpcDeliveryUnknown": false - } - }, "bbbd11388ff3": { "name": "files.read#1", "ordinal": 1, @@ -620,43 +582,6 @@ "isRpcDeliveryUnknown": false } }, - "fb58498a8798": { - "diff": { - "kind": "diff", - "lines": [ - { - "kind": "delete", - "oldLineNumber": 1, - "text": "a" - }, - { - "kind": "add", - "newLineNumber": 1, - "text": "b" - } - ], - "status": "ready", - "truncated": false - }, - "image": { - "dataUri": "data:image/png;base64,aGk=", - "kind": "image", - "status": "ready" - }, - "text": { - "byteLength": { - "$rpc": "undefined" - }, - "content": { - "$rpc": "undefined" - }, - "kind": "file", - "status": "ready", - "truncated": { - "$rpc": "undefined" - } - } - }, "fcbcdb353528": { "name": "files.read#1", "ordinal": 1, @@ -692,6 +617,17 @@ } } }, + "fed544a8befe": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (files.read)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, "ffe1c534d459": { "status": "fulfilled", "startedAt": 0, @@ -738,7 +674,7 @@ "sender": ["43e3786742e9", "96b863d005b1", "096b0c48dd10"], "payloads": ["8423ff93fda2", "1f1a0d8f6723", "ca069263339b"], "settlements": { - "text": "ba9332ae7bb1", + "text": "fed544a8befe", "image": "eee847a9d90d", "diff": "ffe1c534d459" }, @@ -752,7 +688,7 @@ "sender": ["10dde7cd8f0e", "96b863d005b1", "096b0c48dd10"], "payloads": ["8423ff93fda2", "1f1a0d8f6723", "ca069263339b"], "settlements": { - "text": "a7c7f43265d5", + "text": "fed544a8befe", "image": "eee847a9d90d", "diff": "ffe1c534d459" }, @@ -766,11 +702,11 @@ "sender": ["fcbcdb353528", "96b863d005b1", "096b0c48dd10"], "payloads": ["8423ff93fda2", "1f1a0d8f6723", "ca069263339b"], "settlements": { - "text": "5a33eeedb90f", + "text": "fed544a8befe", "image": "eee847a9d90d", "diff": "ffe1c534d459" }, - "state": "fb58498a8798", + "state": "3073ceba86bd", "effects": [] } }, @@ -780,11 +716,11 @@ "sender": ["d7d97e5dac06", "96b863d005b1", "096b0c48dd10"], "payloads": ["8423ff93fda2", "1f1a0d8f6723", "ca069263339b"], "settlements": { - "text": "5a33eeedb90f", + "text": "fed544a8befe", "image": "eee847a9d90d", "diff": "ffe1c534d459" }, - "state": "fb58498a8798", + "state": "3073ceba86bd", "effects": [] } }, @@ -794,11 +730,11 @@ "sender": ["072647254b82", "96b863d005b1", "096b0c48dd10"], "payloads": ["8423ff93fda2", "1f1a0d8f6723", "ca069263339b"], "settlements": { - "text": "5a33eeedb90f", + "text": "fed544a8befe", "image": "eee847a9d90d", "diff": "ffe1c534d459" }, - "state": "fb58498a8798", + "state": "3073ceba86bd", "effects": [] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.readpreview-1.json b/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.readpreview-1.json index e125a9ab5ed..8ad27276644 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.readpreview-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.readpreview-1.json @@ -3,7 +3,7 @@ "family": "files.tab-doc", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", @@ -93,16 +93,6 @@ "ordinal": 4, "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"files.readPreview\",\"params\":{\"worktree\":\"id:workspace-1\",\"relativePath\":\"docs/logo.png\"}}" }, - "2e3bb1c16607": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "TypeError", - "message": "Cannot read properties of undefined (reading 'isImage')", - "isRpcDeliveryUnknown": false - } - }, "32a7c0ae7918": { "status": "rejected", "startedAt": 0, @@ -279,6 +269,17 @@ } } }, + "79a49cf80ecf": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (files.readPreview)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, "8423ff93fda2": { "name": "files.read#1", "ordinal": 2, @@ -453,16 +454,6 @@ } } }, - "c38abcaf69dd": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "Error", - "message": "binary_file", - "isRpcDeliveryUnknown": false - } - }, "c7584e82c72f": { "status": "rejected", "startedAt": 0, @@ -514,16 +505,6 @@ } } }, - "d6234620430f": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "TypeError", - "message": "Cannot read properties of null (reading 'isImage')", - "isRpcDeliveryUnknown": false - } - }, "ddb0f42d762d": { "name": "files.readPreview#1", "ordinal": 3, @@ -696,7 +677,7 @@ "payloads": ["8423ff93fda2", "1f1a0d8f6723", "ca069263339b"], "settlements": { "text": "b5c68b76c498", - "image": "2e3bb1c16607", + "image": "79a49cf80ecf", "diff": "ffe1c534d459" }, "state": "5521ad94c331", @@ -710,7 +691,7 @@ "payloads": ["8423ff93fda2", "1f1a0d8f6723", "ca069263339b"], "settlements": { "text": "b5c68b76c498", - "image": "d6234620430f", + "image": "79a49cf80ecf", "diff": "ffe1c534d459" }, "state": "5521ad94c331", @@ -724,7 +705,7 @@ "payloads": ["8423ff93fda2", "1f1a0d8f6723", "ca069263339b"], "settlements": { "text": "b5c68b76c498", - "image": "c38abcaf69dd", + "image": "79a49cf80ecf", "diff": "ffe1c534d459" }, "state": "5521ad94c331", @@ -738,7 +719,7 @@ "payloads": ["8423ff93fda2", "1f1a0d8f6723", "ca069263339b"], "settlements": { "text": "b5c68b76c498", - "image": "c38abcaf69dd", + "image": "79a49cf80ecf", "diff": "ffe1c534d459" }, "state": "5521ad94c331", @@ -752,7 +733,7 @@ "payloads": ["8423ff93fda2", "1f1a0d8f6723", "ca069263339b"], "settlements": { "text": "b5c68b76c498", - "image": "c38abcaf69dd", + "image": "79a49cf80ecf", "diff": "ffe1c534d459" }, "state": "5521ad94c331", diff --git a/mobile/rpc-foundation/goldens/matrix-files.tab-doc-git.diff-1.json b/mobile/rpc-foundation/goldens/matrix-files.tab-doc-git.diff-1.json index d4034bf5710..430a2fcd3fd 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.tab-doc-git.diff-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.tab-doc-git.diff-1.json @@ -3,7 +3,7 @@ "family": "files.tab-doc", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", @@ -177,26 +177,6 @@ "isRpcDeliveryUnknown": false } }, - "3a9e5c87d18b": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "TypeError", - "message": "Cannot read properties of undefined (reading 'kind')", - "isRpcDeliveryUnknown": false - } - }, - "559c313a79f9": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "TypeError", - "message": "Cannot read properties of null (reading 'kind')", - "isRpcDeliveryUnknown": false - } - }, "6b721f327587": { "name": "git.diff#1", "ordinal": 5, @@ -235,6 +215,17 @@ "ordinal": 2, "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"files.read\",\"params\":{\"worktree\":\"id:workspace-1\",\"relativePath\":\"docs/readme.md\"}}" }, + "8692c1e96521": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (git.diff)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, "96b863d005b1": { "name": "files.readPreview#1", "ordinal": 3, @@ -377,16 +368,6 @@ "isRpcDeliveryUnknown": false } }, - "c38abcaf69dd": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "Error", - "message": "binary_file", - "isRpcDeliveryUnknown": false - } - }, "c7584e82c72f": { "status": "rejected", "startedAt": 0, @@ -695,7 +676,7 @@ "settlements": { "text": "b5c68b76c498", "image": "eee847a9d90d", - "diff": "3a9e5c87d18b" + "diff": "8692c1e96521" }, "state": "e3995cc146f1", "effects": [] @@ -709,7 +690,7 @@ "settlements": { "text": "b5c68b76c498", "image": "eee847a9d90d", - "diff": "559c313a79f9" + "diff": "8692c1e96521" }, "state": "e3995cc146f1", "effects": [] @@ -723,7 +704,7 @@ "settlements": { "text": "b5c68b76c498", "image": "eee847a9d90d", - "diff": "c38abcaf69dd" + "diff": "8692c1e96521" }, "state": "e3995cc146f1", "effects": [] @@ -737,7 +718,7 @@ "settlements": { "text": "b5c68b76c498", "image": "eee847a9d90d", - "diff": "c38abcaf69dd" + "diff": "8692c1e96521" }, "state": "e3995cc146f1", "effects": [] @@ -751,7 +732,7 @@ "settlements": { "text": "b5c68b76c498", "image": "eee847a9d90d", - "diff": "c38abcaf69dd" + "diff": "8692c1e96521" }, "state": "e3995cc146f1", "effects": [] diff --git a/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.open-1.json b/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.open-1.json index c67e146f379..6ef3799be3d 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.open-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.open-1.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.resolveterminalpath-1.json b/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.resolveterminalpath-1.json index 81e5a24e0e5..0552e0397c8 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.resolveterminalpath-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.resolveterminalpath-1.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.json b/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.json index 03b994934f0..8f8984e68ff 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.json index 8547f875930..37d2b8373a1 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json index 25646a38e7b..8959874ae8a 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-git.branch-diff-preview-git.branchdiff-1.json b/mobile/rpc-foundation/goldens/matrix-git.branch-diff-preview-git.branchdiff-1.json index 977e1b80e49..422c9e18ed7 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.branch-diff-preview-git.branchdiff-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.branch-diff-preview-git.branchdiff-1.json @@ -3,7 +3,7 @@ "family": "git.branch-diff-preview", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.branchcompare-1.json b/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.branchcompare-1.json index 9c9519dd5d4..42fabf5168c 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.branchcompare-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.branchcompare-1.json @@ -3,7 +3,7 @@ "family": "git.changes-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.status-1.json b/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.status-1.json index 62ad930c670..a6ee1a50c1f 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.status-1.json @@ -3,7 +3,7 @@ "family": "git.changes-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.changes-load-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-git.changes-load-repo.list-1.json index 17ccd951f1b..09239788301 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.changes-load-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.changes-load-repo.list-1.json @@ -3,7 +3,7 @@ "family": "git.changes-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.changes-load-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-git.changes-load-worktree.show-1.json index 9b36da86669..b66e74626f7 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.changes-load-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.changes-load-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "git.changes-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json b/mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json index 88ba8451bcc..f23aa91d930 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json @@ -3,7 +3,7 @@ "family": "git.commit-message-ai", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.commitcompare-1.json b/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.commitcompare-1.json index c88a57bc9ac..fa029a9e6aa 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.commitcompare-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.commitcompare-1.json @@ -3,7 +3,7 @@ "family": "git.history-commit-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.history-1.json b/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.history-1.json index c5ef406878f..590ef4a9b3b 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.history-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.history-1.json @@ -3,7 +3,7 @@ "family": "git.history-commit-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json b/mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json index 2c3bc4479d5..777b01c76c8 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json @@ -3,7 +3,7 @@ "family": "git.history-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.json b/mobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.json index 54d24163824..6aeea3a843d 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.json @@ -3,7 +3,7 @@ "family": "git.remote-prerequisite", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.json b/mobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.json index 8c3833a01aa..a2927ab6c9a 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addissuecomment-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addissuecomment-1.json index 61fddd15d64..15e03d314c7 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addissuecomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addissuecomment-1.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addprreviewcommentreply-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addprreviewcommentreply-1.json index 1b41fb135c0..18d5661310f 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addprreviewcommentreply-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addprreviewcommentreply-1.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.deleteissuecommentbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.deleteissuecommentbyslug-1.json index 72eed2e4103..5897217930b 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.deleteissuecommentbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.deleteissuecommentbyslug-1.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.updateissuecommentbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.updateissuecommentbyslug-1.json index 147c3265b75..c95201e1542 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.updateissuecommentbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.updateissuecommentbyslug-1.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.resolvereviewthread-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.resolvereviewthread-1.json index 12c1edeab21..22ab073e5d4 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.resolvereviewthread-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.resolvereviewthread-1.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.mergepr-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.mergepr-1.json index bd40b27c25c..d90e325a31f 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.mergepr-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.mergepr-1.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.removeprreviewers-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.removeprreviewers-1.json index 30796255b48..3b6e83fa3b1 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.removeprreviewers-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.removeprreviewers-1.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.requestprreviewers-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.requestprreviewers-1.json index 880018bfcd7..d60594a638d 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.requestprreviewers-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.requestprreviewers-1.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.rerunprchecks-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.rerunprchecks-1.json index 835342d4fee..448ad27cc1f 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.rerunprchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.rerunprchecks-1.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.setprautomerge-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.setprautomerge-1.json index f33638d28bf..0d198da942e 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.setprautomerge-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.setprautomerge-1.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.updateprstate-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.updateprstate-1.json index 0ba3be45f06..071f78ecd2a 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.updateprstate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.updateprstate-1.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.listassignableusers-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.listassignableusers-1.json index 9de6c1c40a6..97d1fca12df 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.listassignableusers-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.listassignableusers-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prcheckdetails-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prcheckdetails-1.json index ad34dfdeefb..33314251451 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prcheckdetails-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prcheckdetails-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prchecks-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prchecks-1.json index b2b41509fc6..4bf5e12b743 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prchecks-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prforbranch-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prforbranch-1.json index a4c56a7cdef..ae97ece0e97 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prforbranch-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prforbranch-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.reposlug-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.reposlug-1.json index b3b8d4d7180..fb1190ffc3c 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.reposlug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.reposlug-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.workitemdetails-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.workitemdetails-1.json index 9d0f50c0523..e02c1d6a41c 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.workitemdetails-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.workitemdetails-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-hostedreview.forbranch-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-hostedreview.forbranch-1.json index cb39134ab65..2f4d99e39ff 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-hostedreview.forbranch-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-hostedreview.forbranch-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-title-mutation-github.updateprtitle-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-title-mutation-github.updateprtitle-1.json index 61201f95439..e5baef40307 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-title-mutation-github.updateprtitle-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-title-mutation-github.updateprtitle-1.json @@ -3,7 +3,7 @@ "family": "github.pr-title-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-home.host-accounts-accounts.list-1.json b/mobile/rpc-foundation/goldens/matrix-home.host-accounts-accounts.list-1.json index e4961b2719b..d0c02061fae 100644 --- a/mobile/rpc-foundation/goldens/matrix-home.host-accounts-accounts.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-home.host-accounts-accounts.list-1.json @@ -3,7 +3,7 @@ "family": "home.host-accounts", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c632fdbc4b730777ecb09f08bec14cca0586042b01ed99d40d0228806c7def4a", diff --git a/mobile/rpc-foundation/goldens/matrix-home.host-stats-stats.summary-1.json b/mobile/rpc-foundation/goldens/matrix-home.host-stats-stats.summary-1.json index a6e515a6389..cca6825e42b 100644 --- a/mobile/rpc-foundation/goldens/matrix-home.host-stats-stats.summary-1.json +++ b/mobile/rpc-foundation/goldens/matrix-home.host-stats-stats.summary-1.json @@ -3,7 +3,7 @@ "family": "home.host-stats", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a9e0780298a1443664e7ae02056168aa34d67556c9c056d51a82c7b4a73ad35b", diff --git a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-1.json b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-1.json index 6fcc70596b4..02a9358f688 100644 --- a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-1.json @@ -3,7 +3,7 @@ "family": "host-worktree-refresh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-2.json b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-2.json index 4ff98187ada..aecb353baf4 100644 --- a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-2.json +++ b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-2.json @@ -3,7 +3,7 @@ "family": "host-worktree-refresh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-3.json b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-3.json index 69d091a5731..b8cf54759ac 100644 --- a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-3.json +++ b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-3.json @@ -3,7 +3,7 @@ "family": "host-worktree-refresh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-2-1.json b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-2-1.json index b7982cd1963..931052948a6 100644 --- a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-2-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-2-1.json @@ -3,7 +3,7 @@ "family": "host-worktree-refresh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.get-1.json b/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.get-1.json index e207bddfe97..9db62cad8cf 100644 --- a/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.get-1.json @@ -3,7 +3,7 @@ "family": "host.view-settings", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a9e0780298a1443664e7ae02056168aa34d67556c9c056d51a82c7b4a73ad35b", diff --git a/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.set-1.json b/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.set-1.json index e3c3f78bc30..3908471ac32 100644 --- a/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.set-1.json @@ -3,7 +3,7 @@ "family": "host.view-settings", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a9e0780298a1443664e7ae02056168aa34d67556c9c056d51a82c7b4a73ad35b", diff --git a/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.activate-1.json b/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.activate-1.json index 97aef5cc561..062d53e65fc 100644 --- a/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.activate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.activate-1.json @@ -3,7 +3,7 @@ "family": "host.worktree-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "92c29bd78ca0c0d5917e9386fc447bb9a1698b1d1ffaba0db7546eaac60da639", diff --git a/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.rm-1.json b/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.rm-1.json index bf618e0c183..5ca3f3a321b 100644 --- a/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.rm-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.rm-1.json @@ -3,7 +3,7 @@ "family": "host.worktree-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "92c29bd78ca0c0d5917e9386fc447bb9a1698b1d1ffaba0db7546eaac60da639", diff --git a/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.set-1.json b/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.set-1.json index 36f03243e4d..d24ad24deec 100644 --- a/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.set-1.json @@ -3,7 +3,7 @@ "family": "host.worktree-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "92c29bd78ca0c0d5917e9386fc447bb9a1698b1d1ffaba0db7546eaac60da639", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.json index 040661657bb..7339d2b28ec 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.json index b99ce9c31a5..64d3e2870eb 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json index c749e7d5409..22b4253f74e 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.json index 81b2109d230..37d90f497f0 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.json index 78749d8d575..35ade29c97e 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json index e2098e6afa4..dc7a8e48217 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json index 1cf8d4eb769..706c34e0b3d 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.json index f0a261c8196..75e8b253acd 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.json index f15cfaa35cd..79aa8cacae7 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.json index d823fb16896..d68bae9f3d4 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.json index 0a4020a7061..93c39dc0f20 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json index 6f46787861c..886bd5e50b9 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json index 5827fabba52..47d504c743a 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.json index e301c17084d..d785e21db8c 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.json index 18f4447597d..2482c3f92bc 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.json index f736250084f..4cbafa01954 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.eligibility", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.json b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.json index b61dc48bf6c..7656477d1ab 100644 --- a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.json +++ b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.json b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.json index 36994bfc203..99f0beb11de 100644 --- a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.json +++ b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json index 0cf82b9b9f6..2b194734dbb 100644 --- a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json +++ b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json index 3ef9f1123a1..ae6ab4958ad 100644 --- a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json +++ b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json b/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json index 9d74f74b064..1aacddc5929 100644 --- a/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json @@ -3,7 +3,7 @@ "family": "linear-detail-barrier", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.json b/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.json index 83c1591445b..b1cb622ffc7 100644 --- a/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.json +++ b/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.json @@ -3,7 +3,7 @@ "family": "linear-detail-barrier", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/matrix-linear.select-workspace-picker-linear.selectworkspace-1.json b/mobile/rpc-foundation/goldens/matrix-linear.select-workspace-picker-linear.selectworkspace-1.json index 3f2e988bdce..fd970967ec8 100644 --- a/mobile/rpc-foundation/goldens/matrix-linear.select-workspace-picker-linear.selectworkspace-1.json +++ b/mobile/rpc-foundation/goldens/matrix-linear.select-workspace-picker-linear.selectworkspace-1.json @@ -3,7 +3,7 @@ "family": "linear.select-workspace-picker", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b65996c152b632d553a31e07e31ea5c76f998eada42cf0966923d730da21908f", diff --git a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-1.json b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-1.json index 5d9d129ac36..c482e0c9358 100644 --- a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-1.json +++ b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-1.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-2.json b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-2.json index 5b938b195f9..4ac97e05f1f 100644 --- a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-2.json +++ b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-2.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-2-1.json b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-2-1.json index 3af7a602561..78bbf0e52e6 100644 --- a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-2-1.json +++ b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-2-1.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-1.json index 9b22e11e6eb..53667dca0fe 100644 --- a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-2.json b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-2.json index 7552e195797..7579662ab25 100644 --- a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-2.json +++ b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-2.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-3.json b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-3.json index 431e5026b98..39c54f5e65b 100644 --- a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-3.json +++ b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-3.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-1.json index 76d43b423ae..429db450e4a 100644 --- a/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-2.json b/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-2.json index 56138d86639..9a3b673ee82 100644 --- a/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-2.json +++ b/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-2.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/matrix-nativechat.image-upload-clipboard.startimageupload-1.json b/mobile/rpc-foundation/goldens/matrix-nativechat.image-upload-clipboard.startimageupload-1.json index 277377aa69a..9a878191a87 100644 --- a/mobile/rpc-foundation/goldens/matrix-nativechat.image-upload-clipboard.startimageupload-1.json +++ b/mobile/rpc-foundation/goldens/matrix-nativechat.image-upload-clipboard.startimageupload-1.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/matrix-nativechat.session-option-pick-settings.mutatenativechatsessionoptions-1.json b/mobile/rpc-foundation/goldens/matrix-nativechat.session-option-pick-settings.mutatenativechatsessionoptions-1.json index b6728a5853e..d33b37c2e4a 100644 --- a/mobile/rpc-foundation/goldens/matrix-nativechat.session-option-pick-settings.mutatenativechatsessionoptions-1.json +++ b/mobile/rpc-foundation/goldens/matrix-nativechat.session-option-pick-settings.mutatenativechatsessionoptions-1.json @@ -3,7 +3,7 @@ "family": "nativeChat.session-option-pick", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-orchestration.workerterminaluserinput-1.json index 40aaabd7a83..ec53d971955 100644 --- a/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-terminal.send-1.json index cd64d510252..d501347b7a6 100644 --- a/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.getmissedsince-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.getmissedsince-1.json index 6323e995659..2805c82accc 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.getmissedsince-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.getmissedsince-1.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-1.json index 7656a5c6038..202f36e9a8b 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-1.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-2.json b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-2.json index 10dfd7730e9..beb936438e7 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-2.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-2.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.unsubscribe-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.unsubscribe-1.json index 3e45c51d260..4d9e729b0be 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.unsubscribe-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.unsubscribe-1.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.display-test-screen-notifications.testpush-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.display-test-screen-notifications.testpush-1.json index de6ea78eb8a..f02050c95d8 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.display-test-screen-notifications.testpush-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.display-test-screen-notifications.testpush-1.json @@ -3,7 +3,7 @@ "family": "notifications.display-test-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "12a04986290715d5db23a1eb5192c1138bb2d375d1b1dd37ba310ea89eb11566", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.push-dismissal-notifications.getmissedsince-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.push-dismissal-notifications.getmissedsince-1.json index e80f44c38c0..854b254b142 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.push-dismissal-notifications.getmissedsince-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.push-dismissal-notifications.getmissedsince-1.json @@ -3,7 +3,7 @@ "family": "notifications.push-dismissal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "595a3eb2994d0596b9fcd0707b175b4e978625053dfbc5c541b0350c0cbfb524", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.registerpush-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.registerpush-1.json index fb068951c77..0fb3537564a 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.registerpush-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.registerpush-1.json @@ -3,7 +3,7 @@ "family": "notifications.push-registration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2e3d939dc162dbc5a38d8a7207111688204a825fd70348721917b3016e1c9470", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.unregisterpush-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.unregisterpush-1.json index 0dc93d80693..c164f461ddf 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.unregisterpush-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.unregisterpush-1.json @@ -3,7 +3,7 @@ "family": "notifications.push-registration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2e3d939dc162dbc5a38d8a7207111688204a825fd70348721917b3016e1c9470", diff --git a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.json b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.json index 21d3ffcde32..4d1bc79aba4 100644 --- a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.json +++ b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.json b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.json index fe2b42f2b01..b2c66dc648f 100644 --- a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.json +++ b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.json b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.json index 060481b347a..79ac33df7eb 100644 --- a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.json +++ b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.json b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.json index a36cb62ca95..70cf49ca8e0 100644 --- a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.json +++ b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json b/mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json index 6d7820eb6f4..420e695b6b1 100644 --- a/mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json @@ -3,7 +3,7 @@ "family": "project-explicit-false", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.json b/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.json index 58f8126e001..c23efdb0485 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.json @@ -3,7 +3,7 @@ "family": "relay.credential-rotation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.json b/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.json index af47ae27088..b1252da9c68 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.json @@ -3,7 +3,7 @@ "family": "relay.credential-rotation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.json b/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.json index 3e43530e26b..2eb48b76687 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.json @@ -3,7 +3,7 @@ "family": "relay.credential-rotation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.json b/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.json index b909a3980a0..dc1b7267bb4 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.json @@ -3,7 +3,7 @@ "family": "relay.direct-upgrade", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.json b/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.json index 97a2f5b57a7..9898c3eb5dd 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.json @@ -3,7 +3,7 @@ "family": "relay.direct-upgrade", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.json b/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.json index f31c49e4347..49a68b22d67 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.json @@ -3,7 +3,7 @@ "family": "relay.direct-upgrade", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.json b/mobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.json index f9b57927e16..59e5cd4546a 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.json @@ -3,7 +3,7 @@ "family": "relay.pairing-recovery", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/matrix-session.browser-tab-create-browser.tabcreate-1.json b/mobile/rpc-foundation/goldens/matrix-session.browser-tab-create-browser.tabcreate-1.json index 91353afa64d..94db8134b5a 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.browser-tab-create-browser.tabcreate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.browser-tab-create-browser.tabcreate-1.json @@ -3,7 +3,7 @@ "family": "session.browser-tab-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.content-create-files.createfile-1.json b/mobile/rpc-foundation/goldens/matrix-session.content-create-files.createfile-1.json index fa57c4e84b1..c4da9bf755e 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.content-create-files.createfile-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.content-create-files.createfile-1.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.content-create-files.open-1.json b/mobile/rpc-foundation/goldens/matrix-session.content-create-files.open-1.json index 21e1c4c3020..7422ba0b968 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.content-create-files.open-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.content-create-files.open-1.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.content-create-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-session.content-create-status.get-1.json index 4cbbef5e281..957a3381923 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.content-create-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.content-create-status.get-1.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.content-create-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-session.content-create-worktree.show-1.json index 71c6e971dbe..36476e7f9ea 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.content-create-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.content-create-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", @@ -166,6 +166,14 @@ } } }, + "37aff7a97a33": { + "createError": "The host sent a reply this app could not read (worktree.show)", + "creatingBrowser": false, + "creatingMarkdown": false, + "pendingBrowserFocusPageId": { + "$rpc": "null" + } + }, "3e7bfd4c59c3": { "name": "files.open#1", "ordinal": 7, @@ -341,14 +349,6 @@ "message": "Couldn't verify the SSH connection. Reconnect the host and try again." } }, - "7822fc8c989d": { - "createError": "Cannot read properties of undefined (reading 'worktree')", - "creatingBrowser": false, - "creatingMarkdown": false, - "pendingBrowserFocusPageId": { - "$rpc": "null" - } - }, "84c6d4a53548": { "name": "worktree.show#1", "ordinal": 3, @@ -384,13 +384,6 @@ } } }, - "8bebb9b2b076": { - "name": "toast", - "ordinal": 5, - "value": { - "message": "Cannot read properties of undefined (reading 'worktree')" - } - }, "9dc70bb3c6c3": { "name": "worktree.show#1", "ordinal": 3, @@ -555,12 +548,11 @@ "$rpc": "null" } }, - "d7217a17cb5c": { - "createError": "Cannot read properties of null (reading 'worktree')", - "creatingBrowser": false, - "creatingMarkdown": false, - "pendingBrowserFocusPageId": { - "$rpc": "null" + "d5e6e519e4a5": { + "name": "toast", + "ordinal": 5, + "value": { + "message": "The host sent a reply this app could not read (worktree.show)" } }, "e2ba4604deae": { @@ -620,13 +612,6 @@ "$rpc": "undefined" } }, - "f02973af22c4": { - "name": "toast", - "ordinal": 5, - "value": { - "message": "Cannot read properties of null (reading 'worktree')" - } - }, "f1279bf9f173": { "name": "files.createFile#1", "ordinal": 6, @@ -656,8 +641,8 @@ "settlements": { "markdown": "eb79a9b3682a" }, - "state": "7822fc8c989d", - "effects": ["8bebb9b2b076"] + "state": "37aff7a97a33", + "effects": ["d5e6e519e4a5"] } }, { @@ -668,8 +653,8 @@ "settlements": { "markdown": "eb79a9b3682a" }, - "state": "d7217a17cb5c", - "effects": ["f02973af22c4"] + "state": "37aff7a97a33", + "effects": ["d5e6e519e4a5"] } }, { diff --git a/mobile/rpc-foundation/goldens/matrix-session.create-terminal-session.tabs.createterminal-1.json b/mobile/rpc-foundation/goldens/matrix-session.create-terminal-session.tabs.createterminal-1.json index 16d14551029..e9e89946a84 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.create-terminal-session.tabs.createterminal-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.create-terminal-session.tabs.createterminal-1.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/matrix-session.create-terminal-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-session.create-terminal-terminal.send-1.json index 663e07358c5..331a733f381 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.create-terminal-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.create-terminal-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-notes-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-session.diff-notes-worktree.show-1.json index 292dbb3f07d..354427126aa 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-notes-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-notes-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "session.diff-notes", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-review-actions-worktree.set-1.json b/mobile/rpc-foundation/goldens/matrix-session.diff-review-actions-worktree.set-1.json index 764d8927904..242eb523e1a 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-review-actions-worktree.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-review-actions-worktree.set-1.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-review-base-ref-show.json b/mobile/rpc-foundation/goldens/matrix-session.diff-review-base-ref-show.json index 675a69973dd..72d08868efe 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-review-base-ref-show.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-review-base-ref-show.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.branchcompare-1.json b/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.branchcompare-1.json index 5c3e408d40c..6259a7ceed8 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.branchcompare-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.branchcompare-1.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.status-1.json b/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.status-1.json index f074448b367..436e1eda4d3 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.status-1.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-review-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.diff-review-repo.list-1.json index e7ea70f2a27..5f8b892ab79 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-review-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-review-repo.list-1.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-review-review-show.json b/mobile/rpc-foundation/goldens/matrix-session.diff-review-review-show.json index e8c15e75a18..bebd001e2dc 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-review-review-show.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-review-review-show.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-files.read-1.json b/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-files.read-1.json new file mode 100644 index 00000000000..d8024c77990 --- /dev/null +++ b/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-files.read-1.json @@ -0,0 +1,619 @@ +{ + "operation": "session.tab-documents", + "family": "session.markdown-disk-fallback", + "namedDeltas": [], + "runnerVersion": 1, + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", + "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", + "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", + "scenarioSha256": "c4b6c9c21433876e2bb3822f289627e7c5607830559289d35cae066ff895fc23", + "platform": "darwin", + "scenarioVersion": 1, + "projectionVersion": 2, + "goldenFormatVersion": 5, + "values": { + "1814b29b1212": { + "name": "files.read#1", + "ordinal": 3, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "refused", + "message": "" + }, + "id": "frame-2", + "ok": false + } + } + }, + "232de05393a9": { + "name": "files.read#1", + "ordinal": 3, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "$rpc": "null" + } + } + } + }, + "26b8fd580c90": { + "name": "files.read#1", + "ordinal": 3, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true + } + } + }, + "411d336833d5": { + "name": "files.read#1", + "ordinal": 3, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "error": { + "message": "inner refused" + }, + "ok": false + } + } + } + }, + "44abbe2af7aa": { + "name": "markdown.readTab#1", + "ordinal": 2, + "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"markdown.readTab\",\"params\":{\"worktree\":\"id:workspace-1\",\"tabId\":\"tab-md\"}}" + }, + "455401ec3b87": { + "name": "files.read#1", + "ordinal": 3, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "", + "isRpcDeliveryUnknown": true + } + } + }, + "5d8fad85cc4d": { + "name": "files.read#1", + "ordinal": 3, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "refused", + "message": "outer refused" + }, + "id": "frame-2", + "ok": false + } + } + }, + "767ef7fdc9b3": { + "name": "files.read#1", + "ordinal": 3, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "method_not_found", + "message": "Unknown method" + }, + "id": "frame-2", + "ok": false + } + } + }, + "877720375363": { + "file": {}, + "markdown": { + "tab-md": { + "message": "Couldn't load markdown", + "status": "error" + } + } + }, + "c681a5b94b73": { + "name": "files.read#1", + "ordinal": 3, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "error": "refused" + } + } + } + }, + "c6cea5310098": { + "file": {}, + "markdown": { + "tab-md": { + "baseVersion": "", + "content": "# disk", + "editable": false, + "isDirty": false, + "localContent": "# disk", + "readOnlyReason": "Editing needs Orca desktop running.", + "stale": false, + "status": "ready" + } + } + }, + "d4be01606497": { + "name": "files.read#1", + "ordinal": 4, + "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"files.read\",\"params\":{\"worktree\":\"id:workspace-1\",\"relativePath\":\"docs/readme.md\"}}" + }, + "d59404d25d6c": { + "name": "files.read#1", + "ordinal": 3, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "byteLength": 6, + "content": "# disk", + "truncated": false + } + } + } + }, + "e258dafc91f1": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "renderer_unavailable", + "message": "Renderer unavailable" + }, + "id": "frame-1", + "ok": false + } + } + }, + "e4cc82795cc4": { + "name": "files.read#1", + "ordinal": 3, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "error": "inner refused", + "ok": false + } + } + } + }, + "e6ef1892cf5d": { + "name": "files.read#1", + "ordinal": 3, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "transport failure", + "isRpcDeliveryUnknown": true + } + } + }, + "eb79a9b3682a": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "$rpc": "undefined" + } + } + }, + "recording": { + "scenario": "matrix-session.markdown-disk-fallback-files.read-1", + "checkpoints": [ + { + "id": "session-markdown-disk-read.normal:fell-back", + "observation": { + "sender": ["e258dafc91f1", "d59404d25d6c"], + "payloads": ["44abbe2af7aa", "d4be01606497"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "c6cea5310098", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.result-absent:fell-back", + "observation": { + "sender": ["e258dafc91f1", "26b8fd580c90"], + "payloads": ["44abbe2af7aa", "d4be01606497"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.result-null:fell-back", + "observation": { + "sender": ["e258dafc91f1", "232de05393a9"], + "payloads": ["44abbe2af7aa", "d4be01606497"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.inner-ok-missing:fell-back", + "observation": { + "sender": ["e258dafc91f1", "c681a5b94b73"], + "payloads": ["44abbe2af7aa", "d4be01606497"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.inner-false-string-error:fell-back", + "observation": { + "sender": ["e258dafc91f1", "e4cc82795cc4"], + "payloads": ["44abbe2af7aa", "d4be01606497"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.inner-false-object-error:fell-back", + "observation": { + "sender": ["e258dafc91f1", "411d336833d5"], + "payloads": ["44abbe2af7aa", "d4be01606497"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.outer-refused:fell-back", + "observation": { + "sender": ["e258dafc91f1", "5d8fad85cc4d"], + "payloads": ["44abbe2af7aa", "d4be01606497"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.outer-refused-no-message:fell-back", + "observation": { + "sender": ["e258dafc91f1", "1814b29b1212"], + "payloads": ["44abbe2af7aa", "d4be01606497"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.method-not-found:fell-back", + "observation": { + "sender": ["e258dafc91f1", "767ef7fdc9b3"], + "payloads": ["44abbe2af7aa", "d4be01606497"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.transport-rejection:fell-back", + "observation": { + "sender": ["e258dafc91f1", "e6ef1892cf5d"], + "payloads": ["44abbe2af7aa", "d4be01606497"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.transport-rejection-no-message:fell-back", + "observation": { + "sender": ["e258dafc91f1", "455401ec3b87"], + "payloads": ["44abbe2af7aa", "d4be01606497"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + } + ] + } +} diff --git a/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-markdown.readtab-1.json b/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-markdown.readtab-1.json new file mode 100644 index 00000000000..8c9d60ceca9 --- /dev/null +++ b/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-markdown.readtab-1.json @@ -0,0 +1,581 @@ +{ + "operation": "session.tab-documents", + "family": "session.markdown-disk-fallback", + "namedDeltas": [], + "runnerVersion": 1, + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", + "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", + "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", + "scenarioSha256": "42ddfcc6c8cac61e5891b72fbac9d04763fa9c52d0cb4f44e1044714a8a1ee77", + "platform": "darwin", + "scenarioVersion": 1, + "projectionVersion": 2, + "goldenFormatVersion": 5, + "values": { + "0d6b0adc036f": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "content": "# served", + "editable": true, + "isDirty": false, + "version": "v1" + } + } + } + }, + "11d4233470ab": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "refused", + "message": "outer refused" + }, + "id": "frame-1", + "ok": false + } + } + }, + "27c8f1a219da": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "method_not_found", + "message": "Unknown method" + }, + "id": "frame-1", + "ok": false + } + } + }, + "30ca4532d665": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true + } + } + }, + "3e3efaab3a20": { + "file": {}, + "markdown": { + "tab-md": { + "baseVersion": "v1", + "content": "# served", + "editable": true, + "isDirty": false, + "localContent": "# served", + "readOnlyReason": { + "$rpc": "undefined" + }, + "stale": false, + "status": "ready" + } + } + }, + "3f9b64bce472": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "", + "isRpcDeliveryUnknown": true + } + } + }, + "44abbe2af7aa": { + "name": "markdown.readTab#1", + "ordinal": 2, + "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"markdown.readTab\",\"params\":{\"worktree\":\"id:workspace-1\",\"tabId\":\"tab-md\"}}" + }, + "4b881f02b557": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "refused", + "message": "" + }, + "id": "frame-1", + "ok": false + } + } + }, + "7316598fc7ff": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "$rpc": "null" + } + } + } + }, + "877720375363": { + "file": {}, + "markdown": { + "tab-md": { + "message": "Couldn't load markdown", + "status": "error" + } + } + }, + "8f5af40a86f8": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "transport failure", + "isRpcDeliveryUnknown": true + } + } + }, + "9678ee74c12b": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "error": { + "message": "inner refused" + }, + "ok": false + } + } + } + }, + "a2bc1f147779": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "error": "inner refused", + "ok": false + } + } + } + }, + "dec5db38cd1c": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "error": "refused" + } + } + } + }, + "eb79a9b3682a": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "$rpc": "undefined" + } + } + }, + "recording": { + "scenario": "matrix-session.markdown-disk-fallback-markdown.readtab-1", + "checkpoints": [ + { + "id": "session-markdown-disk-read.normal:fell-back", + "observation": { + "sender": ["0d6b0adc036f"], + "payloads": ["44abbe2af7aa"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "3e3efaab3a20", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.result-absent:fell-back", + "observation": { + "sender": ["30ca4532d665"], + "payloads": ["44abbe2af7aa"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.result-null:fell-back", + "observation": { + "sender": ["7316598fc7ff"], + "payloads": ["44abbe2af7aa"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.inner-ok-missing:fell-back", + "observation": { + "sender": ["dec5db38cd1c"], + "payloads": ["44abbe2af7aa"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.inner-false-string-error:fell-back", + "observation": { + "sender": ["a2bc1f147779"], + "payloads": ["44abbe2af7aa"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.inner-false-object-error:fell-back", + "observation": { + "sender": ["9678ee74c12b"], + "payloads": ["44abbe2af7aa"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.outer-refused:fell-back", + "observation": { + "sender": ["11d4233470ab"], + "payloads": ["44abbe2af7aa"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.outer-refused-no-message:fell-back", + "observation": { + "sender": ["4b881f02b557"], + "payloads": ["44abbe2af7aa"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.method-not-found:fell-back", + "observation": { + "sender": ["27c8f1a219da"], + "payloads": ["44abbe2af7aa"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.transport-rejection:fell-back", + "observation": { + "sender": ["8f5af40a86f8"], + "payloads": ["44abbe2af7aa"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + }, + { + "id": "session-markdown-disk-read.transport-rejection-no-message:fell-back", + "observation": { + "sender": ["3f9b64bce472"], + "payloads": ["44abbe2af7aa"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "877720375363", + "effects": [] + } + } + ] + } +} diff --git a/mobile/rpc-foundation/goldens/matrix-session.markdown-save-markdown.savetab-1.json b/mobile/rpc-foundation/goldens/matrix-session.markdown-save-markdown.savetab-1.json index efe0539ffba..06d12b655e2 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.markdown-save-markdown.savetab-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.markdown-save-markdown.savetab-1.json @@ -3,7 +3,7 @@ "family": "session.markdown-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.readsession-1.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.readsession-1.json index c7ddee8776a..a1df6833bac 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.readsession-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.readsession-1.json @@ -3,7 +3,7 @@ "family": "session.native-chat-page", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "60ce67f66134d6385aa7fb47f2135436a7b3ce0221e3b2514bea65e06dba5518", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-1-1.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-1-1.json index dffa854d0a1..3961b81f776 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-1-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-1-1.json @@ -3,7 +3,7 @@ "family": "session.native-chat-page", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "60ce67f66134d6385aa7fb47f2135436a7b3ce0221e3b2514bea65e06dba5518", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-2-1.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-2-1.json index 81cd1db8acd..de1821727c7 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-2-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-2-1.json @@ -3,7 +3,7 @@ "family": "session.native-chat-page", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "60ce67f66134d6385aa7fb47f2135436a7b3ce0221e3b2514bea65e06dba5518", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-readability-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-readability-repo.list-1.json index 57dab7fda46..2dabb391413 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-readability-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-readability-repo.list-1.json @@ -3,7 +3,7 @@ "family": "session.native-chat-readability", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-orchestration.workerterminaluserinput-1.json index eb8c4567ac6..9e536ca4313 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "session.native-chat-stop", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-1.json index 08122935f57..e13eca87bc2 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "session.native-chat-stop", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-2.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-2.json index f80eb23436d..e13ed7a96bf 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-2.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-2.json @@ -3,7 +3,7 @@ "family": "session.native-chat-stop", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.branchcompare-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.branchcompare-1.json index b544b481498..d0777ecd384 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.branchcompare-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.branchcompare-1.json @@ -3,7 +3,7 @@ "family": "session.pr-branch-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.status-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.status-1.json index c412f6b5a21..15ced76c11f 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.status-1.json @@ -3,7 +3,7 @@ "family": "session.pr-branch-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-repo.list-1.json index 790106db047..578ba42698b 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-repo.list-1.json @@ -3,7 +3,7 @@ "family": "session.pr-branch-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-worktree.show-1.json index f65b9dc6666..03670230aeb 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "session.pr-branch-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prchecks-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prchecks-1.json index e91d503fb2f..282fa0ea25f 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prchecks-1.json @@ -3,7 +3,7 @@ "family": "session.pr-sidebar", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "87ffa2daea415d2682f1112025b5bd9d52404c6fc7c239180a3ad2120678ff1f", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prforbranch-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prforbranch-1.json index 236bbe89b9a..ee8ac79095c 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prforbranch-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prforbranch-1.json @@ -3,7 +3,7 @@ "family": "session.pr-sidebar", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "87ffa2daea415d2682f1112025b5bd9d52404c6fc7c239180a3ad2120678ff1f", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-hostedreview.forbranch-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-hostedreview.forbranch-1.json index fea60749920..bfaf822a9cf 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-hostedreview.forbranch-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-hostedreview.forbranch-1.json @@ -3,7 +3,7 @@ "family": "session.pr-sidebar", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "87ffa2daea415d2682f1112025b5bd9d52404c6fc7c239180a3ad2120678ff1f", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-worktree.show-1.json index 08927f9c3ca..43b106cb08f 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "session.pr-sidebar", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "87ffa2daea415d2682f1112025b5bd9d52404c6fc7c239180a3ad2120678ff1f", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-triage-session.tabs.createterminal-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-triage-session.tabs.createterminal-1.json index 04c2514219e..578f6f5cd75 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-triage-session.tabs.createterminal-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-triage-session.tabs.createterminal-1.json @@ -3,7 +3,7 @@ "family": "session.pr-triage", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-triage-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-triage-terminal.send-1.json index f957f62dd0e..1da0ae482ae 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-triage-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-triage-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "session.pr-triage", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-branch-diff-git.branchdiff-1.json b/mobile/rpc-foundation/goldens/matrix-session.review-branch-diff-git.branchdiff-1.json index 8ce27c2dd85..35574c74f2f 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-branch-diff-git.branchdiff-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-branch-diff-git.branchdiff-1.json @@ -3,7 +3,7 @@ "family": "session.review-branch-diff", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-1.json b/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-1.json index 215735fe899..cd2b5091286 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-1.json @@ -3,7 +3,7 @@ "family": "session.review-file-diff", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-2.json b/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-2.json index 20bb3f90804..df4117078aa 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-2.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-2.json @@ -3,7 +3,7 @@ "family": "session.review-file-diff", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-3.json b/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-3.json index cb2bf015528..d3204f9059e 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-3.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-3.json @@ -3,7 +3,7 @@ "family": "session.review-file-diff", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.discard-1.json b/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.discard-1.json index 83372cf271a..8bad7c0e511 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.discard-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.discard-1.json @@ -3,7 +3,7 @@ "family": "session.review-git-mutations", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-1.json b/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-1.json index 2321a61cf6e..41fcfd189da 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-1.json @@ -3,7 +3,7 @@ "family": "session.review-git-mutations", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-2.json b/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-2.json index 617b20b7ed1..46fcfac32e0 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-2.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-2.json @@ -3,7 +3,7 @@ "family": "session.review-git-mutations", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-send-sheet-session.tabs.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.review-send-sheet-session.tabs.list-1.json index 81c9c2f3af9..3880f546d96 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-send-sheet-session.tabs.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-send-sheet-session.tabs.list-1.json @@ -3,7 +3,7 @@ "family": "session.review-send-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-1.json b/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-1.json index 89cdce9f5b2..83a29e78e31 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-1.json @@ -3,7 +3,7 @@ "family": "session.startup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "6b08c394e37fd572cf63a4c11934247d379008f11117aa5af33b2926fbd32d1e", diff --git a/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-2.json b/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-2.json index 11248526070..9438ee47df8 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-2.json +++ b/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-2.json @@ -3,7 +3,7 @@ "family": "session.startup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "6b08c394e37fd572cf63a4c11934247d379008f11117aa5af33b2926fbd32d1e", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-activation-session.tabs.activate-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-activation-session.tabs.activate-1.json index 848bff50218..71710e101dc 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-activation-session.tabs.activate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-activation-session.tabs.activate-1.json @@ -3,7 +3,7 @@ "family": "session.tab-activation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-activation-terminal.focus-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-activation-terminal.focus-1.json index 3732bf509f8..c4b9ec2d898 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-activation-terminal.focus-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-activation-terminal.focus-1.json @@ -3,7 +3,7 @@ "family": "session.tab-activation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-close-session-session.tabs.close-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-close-session-session.tabs.close-1.json index 3ff6d8f1b31..b5d27cb7c73 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-close-session-session.tabs.close-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-close-session-session.tabs.close-1.json @@ -3,7 +3,7 @@ "family": "session.tab-close-session", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-close-terminal.close-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-close-terminal.close-1.json index c2804effb53..192fb7703df 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-close-terminal.close-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-close-terminal.close-1.json @@ -3,7 +3,7 @@ "family": "session.tab-close", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-documents-markdown.readtab-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-documents-markdown.readtab-1.json index ea1bb009889..f7f47f2adc1 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-documents-markdown.readtab-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-documents-markdown.readtab-1.json @@ -3,7 +3,7 @@ "family": "session.tab-documents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-rename-terminal.rename-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-rename-terminal.rename-1.json index 534ad58342f..03c1a3e309f 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-rename-terminal.rename-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-rename-terminal.rename-1.json @@ -3,7 +3,7 @@ "family": "session.tab-rename", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.json index e8b7762306d..c4b7c4f4afa 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.json @@ -3,7 +3,7 @@ "family": "session.tab-reveal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json index 3cbcb0653de..e5a21fa55b9 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json @@ -3,7 +3,7 @@ "family": "session.tab-reveal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tabs-stream-health-session.tabs.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.tabs-stream-health-session.tabs.list-1.json index 9f8a0e9fbb9..49aef0cdf6b 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tabs-stream-health-session.tabs.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tabs-stream-health-session.tabs.list-1.json @@ -3,7 +3,7 @@ "family": "session.tabs-stream-health", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-display-mode-terminal.setdisplaymode-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-display-mode-terminal.setdisplaymode-1.json index b115ba054ae..0b93c0c540e 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-display-mode-terminal.setdisplaymode-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-display-mode-terminal.setdisplaymode-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-display-mode", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9e90ad39a8d4257adf30166757a6364c4710dc3ae9f06365f80a3dd8f1c94d89", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-orchestration.workerterminaluserinput-1.json index a26318e0ef3..11011527344 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-gesture-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9d119d5ec320e2538105d6ff673b9f4b8e3decbe46527947489dffbcf2ac0472", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.clearbuffer-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.clearbuffer-1.json index 4912ff790b6..6992251b6e7 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.clearbuffer-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.clearbuffer-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-gesture-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9d119d5ec320e2538105d6ff673b9f4b8e3decbe46527947489dffbcf2ac0472", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.send-1.json index 83649978744..5316724ae6c 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-gesture-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9d119d5ec320e2538105d6ff673b9f4b8e3decbe46527947489dffbcf2ac0472", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-orchestration.workerterminaluserinput-1.json index 2d054b6c055..b07a08c2416 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-input-send", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-terminal.send-1.json index d006f7709f7..ba0dc00f52a 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-input-send", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-inventory-terminal.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-inventory-terminal.list-1.json index edf15c057ba..e129307c92f 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-inventory-terminal.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-inventory-terminal.list-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-orchestration.workerterminaluserinput-1.json index dd73cd8fcec..68b070b95eb 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-settings.get-1.json index 3298c79fe70..a032008296e 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-settings.get-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-terminal.send-1.json index 90c41a4cfc1..0c0fc586054 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-repo.list-1.json index 36b19989309..07a34f934a8 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-repo.list-1.json @@ -3,7 +3,7 @@ "family": "session.worktree-connection", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-settings.get-1.json index 655326b952c..ee3c1cc8904 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-settings.get-1.json @@ -3,7 +3,7 @@ "family": "session.worktree-connection", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.json b/mobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.json index c21a96daec0..fc3b3438521 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json index a221e9d3e9f..a19986021b8 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json index d7359707f9d..b17427b5f7e 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json b/mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json index deb4ea6ce8e..b0cc7bf5b5c 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json @@ -3,7 +3,7 @@ "family": "settings-best-effort", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.json index 4b237295a5c..3919f2304a3 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.json b/mobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.json index 44e85e8a206..4e30db8316f 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.json b/mobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.json index f687bb7e1ce..0a6d55a3d58 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json index b716a340be2..dbd1035e79f 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-preflight.detectagents-1.json b/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-preflight.detectagents-1.json index 908fb93784b..8689147909e 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-preflight.detectagents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-preflight.detectagents-1.json @@ -3,7 +3,7 @@ "family": "settings.new-tab-local-agents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-repo.list-1.json index b8f18a501bf..7ba74819cc0 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-repo.list-1.json @@ -3,7 +3,7 @@ "family": "settings.new-tab-local-agents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-settings.get-1.json index 45bf3e870e7..2b73de8ed70 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.new-tab-local-agents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.getterminalquickcommands-1.json b/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.getterminalquickcommands-1.json index ee04d1b2487..3b49e16a03d 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.getterminalquickcommands-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.getterminalquickcommands-1.json @@ -3,7 +3,7 @@ "family": "settings.quick-commands", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.updateterminalquickcommands-1.json b/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.updateterminalquickcommands-1.json index 221e6e64551..f7c4f78ff01 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.updateterminalquickcommands-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.updateterminalquickcommands-1.json @@ -3,7 +3,7 @@ "family": "settings.quick-commands", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json index 0a1f0749ffb..f1563a76dcc 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json index 804fbc95572..e2c256ff3b2 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json index 8f4483b34b3..c74c8d2ba0e 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json index 4be80938346..7a6e306f68c 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.json index f29e04b6e0a..91bd512d960 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", @@ -457,6 +457,17 @@ } } }, + "9cd99bc29688": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (folderWorkspace.list)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, "a2feaaab3fcf": { "name": "folderWorkspace.list#1", "ordinal": 2, @@ -723,7 +734,7 @@ "1bfeba989b20" ], "settlements": { - "load": "3c70da5d6d8e" + "load": "9cd99bc29688" }, "state": "44136fa355b3", "effects": [] @@ -771,7 +782,7 @@ "1bfeba989b20" ], "settlements": { - "load": "3c70da5d6d8e" + "load": "9cd99bc29688" }, "state": "44136fa355b3", "effects": [] diff --git a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.json index b5a1e91b375..f219ba10b70 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", @@ -590,6 +590,17 @@ } } }, + "c884ec49315c": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (projectGroup.list)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, "cabca99619ed": { "name": "repo.list#1", "ordinal": 6, @@ -723,7 +734,7 @@ "1bfeba989b20" ], "settlements": { - "load": "3c70da5d6d8e" + "load": "c884ec49315c" }, "state": "44136fa355b3", "effects": [] @@ -771,7 +782,7 @@ "1bfeba989b20" ], "settlements": { - "load": "3c70da5d6d8e" + "load": "c884ec49315c" }, "state": "44136fa355b3", "effects": [] diff --git a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.json index 91aefa1fa88..bf4a1d412bc 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", @@ -133,13 +133,13 @@ "ordinal": 7, "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"folderWorkspace.list\"}" }, - "2381a3fe154e": { + "28b1c60e88cb": { "status": "rejected", "startedAt": 0, "settledAt": 0, "error": { - "category": "TypeError", - "message": "Cannot read properties of undefined (reading 'repos')", + "category": "Error", + "message": "The host sent a reply this app could not read (repo.list)", "isRpcDeliveryUnknown": false } }, @@ -211,16 +211,6 @@ "ordinal": 8, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"projectGroup.list\"}" }, - "63dfbb6942f2": { - "status": "rejected", - "startedAt": 0, - "settledAt": 0, - "error": { - "category": "TypeError", - "message": "Cannot read properties of null (reading 'repos')", - "isRpcDeliveryUnknown": false - } - }, "6af91c1da122": { "name": "worktree.ps#1", "ordinal": 5, @@ -793,7 +783,7 @@ "1bfeba989b20" ], "settlements": { - "load": "2381a3fe154e" + "load": "28b1c60e88cb" }, "state": "44136fa355b3", "effects": [] @@ -841,7 +831,7 @@ "1bfeba989b20" ], "settlements": { - "load": "63dfbb6942f2" + "load": "28b1c60e88cb" }, "state": "44136fa355b3", "effects": [] @@ -889,7 +879,7 @@ "1bfeba989b20" ], "settlements": { - "load": "3c70da5d6d8e" + "load": "28b1c60e88cb" }, "state": "44136fa355b3", "effects": [] @@ -937,7 +927,7 @@ "1bfeba989b20" ], "settlements": { - "load": "3c70da5d6d8e" + "load": "28b1c60e88cb" }, "state": "44136fa355b3", "effects": [] @@ -985,7 +975,7 @@ "1bfeba989b20" ], "settlements": { - "load": "3c70da5d6d8e" + "load": "28b1c60e88cb" }, "state": "44136fa355b3", "effects": [] diff --git a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.json index c8b5e6f5c7d..7a4d3819f94 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.json b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.json index 653a3f0f2cb..c0b36902d53 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", @@ -399,6 +399,17 @@ } } }, + "7fb1c6f9f3ae": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "RpcIncompatibleReplyError", + "message": "The host sent a reply this app could not read (worktree.ps)", + "isRpcDeliveryUnknown": false, + "code": "incompatible_reply" + } + }, "82132b79b8f7": { "name": "settings.get#1", "ordinal": 9, @@ -719,7 +730,7 @@ "1bfeba989b20" ], "settlements": { - "load": "0620c0819077" + "load": "7fb1c6f9f3ae" }, "state": "44136fa355b3", "effects": [] @@ -743,7 +754,7 @@ "1bfeba989b20" ], "settlements": { - "load": "0620c0819077" + "load": "7fb1c6f9f3ae" }, "state": "44136fa355b3", "effects": [] diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.json index b2086bae0aa..bae5449a266 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.json index 94569d2b169..2d257800a8a 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.json index cddfb049564..758f2b88c0f 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.json index af687bf83bf..ea17a7ddba1 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json index f083affadd8..b2fa11193c1 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json index aa2e4e4f1d3..ef57b8c85f1 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.json index 6215b297e59..f262c63337b 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json index 609a0d70ee5..3e3be53a599 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.json b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.json index f0381382c07..80f72b2c64d 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.json b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.json index b0f0df3f73c..1972517cb1c 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json index 3491f08db14..db5c291a0b0 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json index 628a7c47d44..89e3ef19d3a 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json index cd67ff0b6eb..6ce514e00b5 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.workspace-submit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.dictation-chunk-speech.dictation.chunk-1.json b/mobile/rpc-foundation/goldens/matrix-speech.dictation-chunk-speech.dictation.chunk-1.json index c9eecb78f73..02c5dfc261c 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.dictation-chunk-speech.dictation.chunk-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.dictation-chunk-speech.dictation.chunk-1.json @@ -3,7 +3,7 @@ "family": "speech.dictation-chunk", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.finish-1.json b/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.finish-1.json index 7242ab948fa..cfb93b27d7a 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.finish-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.finish-1.json @@ -3,7 +3,7 @@ "family": "speech.dictation-session", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.start-1.json b/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.start-1.json index 34cb9c1c8b5..13b16dbe91c 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.start-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.start-1.json @@ -3,7 +3,7 @@ "family": "speech.dictation-session", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.cancel-1.json b/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.cancel-1.json index 97dd9c1b69c..ce8aea374fe 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.cancel-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.cancel-1.json @@ -3,7 +3,7 @@ "family": "speech.dictation-start", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.start-1.json b/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.start-1.json index 5962b8feb8c..ba5767f0abe 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.start-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.start-1.json @@ -3,7 +3,7 @@ "family": "speech.dictation-start", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.dictation.setup-1.json b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.dictation.setup-1.json index 7dd4a487bbf..98662e41971 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.dictation.setup-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.dictation.setup-1.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", @@ -85,35 +85,14 @@ "ordinal": 6, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"speech.models.delete\",\"params\":{\"modelId\":\"whisper-small\"}}" }, - "301151228fa3": { - "status": "fulfilled", + "2d0263e221bc": { + "status": "rejected", "startedAt": 0, "settledAt": 0, - "value": { - "error": "refused" - } - }, - "3033fa217374": { - "configure": { - "error": "inner refused", - "ok": false - }, - "delete": { - "enabled": true, - "models": [], - "selectedModelId": "whisper-small" - }, - "download": "started", - "list": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" + "error": { + "category": "Error", + "message": "The host sent a reply this app could not read (speech.dictation.setup)", + "isRpcDeliveryUnknown": false } }, "32a7c0ae7918": { @@ -169,31 +148,6 @@ } } }, - "43c4c7a19f7e": { - "configure": { - "error": { - "message": "inner refused" - }, - "ok": false - }, - "delete": { - "enabled": true, - "models": [], - "selectedModelId": "whisper-small" - }, - "download": "started", - "list": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - } - }, "4f793e38be31": { "name": "speech.models.list#1", "ordinal": 2, @@ -318,26 +272,6 @@ } } }, - "7af31590ded9": { - "configure": "started", - "delete": { - "enabled": true, - "models": [], - "selectedModelId": "whisper-small" - }, - "download": "started", - "list": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - } - }, "7c5b27891a8f": { "configure": { "enabled": true, @@ -401,15 +335,6 @@ } } }, - "9f00dd54ba64": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "error": "inner refused", - "ok": false - } - }, "a2879fd6371d": { "status": "fulfilled", "startedAt": 0, @@ -510,39 +435,6 @@ "isRpcDeliveryUnknown": true } }, - "a9c35a6f891b": { - "configure": { - "error": "refused" - }, - "delete": { - "enabled": true, - "models": [], - "selectedModelId": "whisper-small" - }, - "download": "started", - "list": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - } - }, - "ad8a954e879d": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "error": { - "message": "inner refused" - }, - "ok": false - } - }, "adbb96fcc08c": { "name": "speech.models.download#1", "ordinal": 3, @@ -620,28 +512,6 @@ "isRpcDeliveryUnknown": false } }, - "be76d126a25c": { - "configure": { - "$rpc": "null" - }, - "delete": { - "enabled": true, - "models": [], - "selectedModelId": "whisper-small" - }, - "download": "started", - "list": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - } - }, "c2b954773835": { "name": "speech.dictation.setup#1", "ordinal": 7, @@ -741,14 +611,6 @@ "isRpcDeliveryUnknown": false } }, - "ee20a1dc39e7": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "$rpc": "null" - } - }, "efb9a676286c": { "delete": { "enabled": true, @@ -847,9 +709,9 @@ "list": "a2879fd6371d", "download": "eb79a9b3682a", "delete": "fc5fb77f49bb", - "configure": "eb79a9b3682a" + "configure": "2d0263e221bc" }, - "state": "7af31590ded9", + "state": "efb9a676286c", "effects": [] } }, @@ -862,9 +724,9 @@ "list": "a2879fd6371d", "download": "eb79a9b3682a", "delete": "fc5fb77f49bb", - "configure": "ee20a1dc39e7" + "configure": "2d0263e221bc" }, - "state": "be76d126a25c", + "state": "efb9a676286c", "effects": [] } }, @@ -877,9 +739,9 @@ "list": "a2879fd6371d", "download": "eb79a9b3682a", "delete": "fc5fb77f49bb", - "configure": "301151228fa3" + "configure": "2d0263e221bc" }, - "state": "a9c35a6f891b", + "state": "efb9a676286c", "effects": [] } }, @@ -892,9 +754,9 @@ "list": "a2879fd6371d", "download": "eb79a9b3682a", "delete": "fc5fb77f49bb", - "configure": "9f00dd54ba64" + "configure": "2d0263e221bc" }, - "state": "3033fa217374", + "state": "efb9a676286c", "effects": [] } }, @@ -907,9 +769,9 @@ "list": "a2879fd6371d", "download": "eb79a9b3682a", "delete": "fc5fb77f49bb", - "configure": "ad8a954e879d" + "configure": "2d0263e221bc" }, - "state": "43c4c7a19f7e", + "state": "efb9a676286c", "effects": [] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.delete-1.json b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.delete-1.json index a783e91a5a0..ec67a796fc7 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.delete-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.delete-1.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", @@ -13,68 +13,11 @@ "projectionVersion": 2, "goldenFormatVersion": 5, "values": { - "0629aa17065f": { - "configure": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - }, - "delete": { - "error": { - "message": "inner refused" - }, - "ok": false - }, - "download": "started", - "list": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - } - }, "0e61f11d0307": { "name": "speech.models.delete#1", "ordinal": 6, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"speech.models.delete\",\"params\":{\"modelId\":\"whisper-small\"}}" }, - "1117d44df3f8": { - "configure": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - }, - "delete": "started", - "download": "started", - "list": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - } - }, "1f67869c3be1": { "configure": { "enabled": true, @@ -137,14 +80,6 @@ } } }, - "301151228fa3": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "error": "refused" - } - }, "32a7c0ae7918": { "status": "rejected", "startedAt": 0, @@ -264,34 +199,6 @@ } } }, - "3eac8959f5ab": { - "configure": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - }, - "delete": { - "$rpc": "null" - }, - "download": "started", - "list": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - } - }, "47237ace2093": { "name": "speech.models.delete#1", "ordinal": 5, @@ -474,15 +381,6 @@ "selectedModelId": "whisper-small" } }, - "9f00dd54ba64": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "error": "inner refused", - "ok": false - } - }, "a2879fd6371d": { "status": "fulfilled", "startedAt": 0, @@ -519,17 +417,6 @@ "isRpcDeliveryUnknown": false } }, - "ad8a954e879d": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "error": { - "message": "inner refused" - }, - "ok": false - } - }, "adbb96fcc08c": { "name": "speech.models.download#1", "ordinal": 3, @@ -619,34 +506,6 @@ } } }, - "d32fe9752c54": { - "configure": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - }, - "delete": { - "error": "refused" - }, - "download": "started", - "list": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - } - }, "d8c329a93e43": { "name": "speech.models.delete#1", "ordinal": 5, @@ -718,35 +577,6 @@ } } }, - "e2401fd120ea": { - "configure": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - }, - "delete": { - "error": "inner refused", - "ok": false - }, - "download": "started", - "list": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - } - }, "e35a372455e8": { "name": "speech.models.delete#1", "ordinal": 5, @@ -782,6 +612,16 @@ } } }, + "e5305075c4c9": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "The host sent a reply this app could not read (speech.models.delete)", + "isRpcDeliveryUnknown": false + } + }, "eb5c8d14744b": { "name": "speech.models.delete#1", "ordinal": 5, @@ -822,14 +662,6 @@ "$rpc": "undefined" } }, - "ee20a1dc39e7": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "$rpc": "null" - } - }, "fba26d61d9d2": { "name": "speech.dictation.setup#1", "ordinal": 8, @@ -872,10 +704,10 @@ "settlements": { "list": "a2879fd6371d", "download": "eb79a9b3682a", - "delete": "eb79a9b3682a", + "delete": "e5305075c4c9", "configure": "a2879fd6371d" }, - "state": "1117d44df3f8", + "state": "1f67869c3be1", "effects": [] } }, @@ -887,10 +719,10 @@ "settlements": { "list": "a2879fd6371d", "download": "eb79a9b3682a", - "delete": "ee20a1dc39e7", + "delete": "e5305075c4c9", "configure": "a2879fd6371d" }, - "state": "3eac8959f5ab", + "state": "1f67869c3be1", "effects": [] } }, @@ -902,10 +734,10 @@ "settlements": { "list": "a2879fd6371d", "download": "eb79a9b3682a", - "delete": "301151228fa3", + "delete": "e5305075c4c9", "configure": "a2879fd6371d" }, - "state": "d32fe9752c54", + "state": "1f67869c3be1", "effects": [] } }, @@ -917,10 +749,10 @@ "settlements": { "list": "a2879fd6371d", "download": "eb79a9b3682a", - "delete": "9f00dd54ba64", + "delete": "e5305075c4c9", "configure": "a2879fd6371d" }, - "state": "e2401fd120ea", + "state": "1f67869c3be1", "effects": [] } }, @@ -932,10 +764,10 @@ "settlements": { "list": "a2879fd6371d", "download": "eb79a9b3682a", - "delete": "ad8a954e879d", + "delete": "e5305075c4c9", "configure": "a2879fd6371d" }, - "state": "0629aa17065f", + "state": "1f67869c3be1", "effects": [] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.download-1.json b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.download-1.json index 1701d568411..5139cbc2cbc 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.download-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.download-1.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.list-1.json b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.list-1.json index 7f556798882..c398c955455 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.list-1.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", @@ -18,79 +18,6 @@ "ordinal": 6, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"speech.models.delete\",\"params\":{\"modelId\":\"whisper-small\"}}" }, - "1885e95050f7": { - "configure": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - }, - "delete": { - "enabled": true, - "models": [], - "selectedModelId": "whisper-small" - }, - "download": "started", - "list": "started" - }, - "26e00f0930ac": { - "configure": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - }, - "delete": { - "enabled": true, - "models": [], - "selectedModelId": "whisper-small" - }, - "download": "started", - "list": { - "$rpc": "null" - } - }, - "26e98969da6c": { - "configure": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - }, - "delete": { - "enabled": true, - "models": [], - "selectedModelId": "whisper-small" - }, - "download": "started", - "list": { - "error": "inner refused", - "ok": false - } - }, - "301151228fa3": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "error": "refused" - } - }, "32a7c0ae7918": { "status": "rejected", "startedAt": 0, @@ -144,29 +71,14 @@ } } }, - "4e80c1e9f058": { - "configure": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - }, - "delete": { - "enabled": true, - "models": [], - "selectedModelId": "whisper-small" - }, - "download": "started", - "list": { - "error": { - "message": "inner refused" - }, - "ok": false + "44b46aebf7db": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "The host sent a reply this app could not read (speech.models.list)", + "isRpcDeliveryUnknown": false } }, "4f793e38be31": { @@ -362,28 +274,6 @@ "selectedModelId": "whisper-small" } }, - "8214effff7c5": { - "configure": { - "enabled": true, - "models": [ - { - "id": "whisper-small", - "name": "Small", - "status": "ready" - } - ], - "selectedModelId": "whisper-small" - }, - "delete": { - "enabled": true, - "models": [], - "selectedModelId": "whisper-small" - }, - "download": "started", - "list": { - "error": "refused" - } - }, "8a419aef0d30": { "name": "speech.models.list#1", "ordinal": 1, @@ -475,15 +365,6 @@ } } }, - "9f00dd54ba64": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "error": "inner refused", - "ok": false - } - }, "a1c6027c24c9": { "name": "speech.models.list#1", "ordinal": 1, @@ -541,17 +422,6 @@ "isRpcDeliveryUnknown": true } }, - "ad8a954e879d": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "error": { - "message": "inner refused" - }, - "ok": false - } - }, "adbb96fcc08c": { "name": "speech.models.download#1", "ordinal": 3, @@ -752,14 +622,6 @@ "$rpc": "undefined" } }, - "ee20a1dc39e7": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "$rpc": "null" - } - }, "fba26d61d9d2": { "name": "speech.dictation.setup#1", "ordinal": 8, @@ -834,12 +696,12 @@ "sender": ["a1c6027c24c9", "adbb96fcc08c", "d8c329a93e43", "38ccf3e1658f"], "payloads": ["4f793e38be31", "694b34cbeee9", "0e61f11d0307", "fba26d61d9d2"], "settlements": { - "list": "eb79a9b3682a", + "list": "44b46aebf7db", "download": "eb79a9b3682a", "delete": "fc5fb77f49bb", "configure": "a2879fd6371d" }, - "state": "1885e95050f7", + "state": "8c2c55317f83", "effects": [] } }, @@ -849,12 +711,12 @@ "sender": ["fcf4e2c83b5a", "adbb96fcc08c", "d8c329a93e43", "38ccf3e1658f"], "payloads": ["4f793e38be31", "694b34cbeee9", "0e61f11d0307", "fba26d61d9d2"], "settlements": { - "list": "ee20a1dc39e7", + "list": "44b46aebf7db", "download": "eb79a9b3682a", "delete": "fc5fb77f49bb", "configure": "a2879fd6371d" }, - "state": "26e00f0930ac", + "state": "8c2c55317f83", "effects": [] } }, @@ -864,12 +726,12 @@ "sender": ["64e366a1c93a", "adbb96fcc08c", "d8c329a93e43", "38ccf3e1658f"], "payloads": ["4f793e38be31", "694b34cbeee9", "0e61f11d0307", "fba26d61d9d2"], "settlements": { - "list": "301151228fa3", + "list": "44b46aebf7db", "download": "eb79a9b3682a", "delete": "fc5fb77f49bb", "configure": "a2879fd6371d" }, - "state": "8214effff7c5", + "state": "8c2c55317f83", "effects": [] } }, @@ -879,12 +741,12 @@ "sender": ["8a419aef0d30", "adbb96fcc08c", "d8c329a93e43", "38ccf3e1658f"], "payloads": ["4f793e38be31", "694b34cbeee9", "0e61f11d0307", "fba26d61d9d2"], "settlements": { - "list": "9f00dd54ba64", + "list": "44b46aebf7db", "download": "eb79a9b3682a", "delete": "fc5fb77f49bb", "configure": "a2879fd6371d" }, - "state": "26e98969da6c", + "state": "8c2c55317f83", "effects": [] } }, @@ -894,12 +756,12 @@ "sender": ["94dfe144483b", "adbb96fcc08c", "d8c329a93e43", "38ccf3e1658f"], "payloads": ["4f793e38be31", "694b34cbeee9", "0e61f11d0307", "fba26d61d9d2"], "settlements": { - "list": "ad8a954e879d", + "list": "44b46aebf7db", "download": "eb79a9b3682a", "delete": "fc5fb77f49bb", "configure": "a2879fd6371d" }, - "state": "4e80c1e9f058", + "state": "8c2c55317f83", "effects": [] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.addprreviewcomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.addprreviewcomment-1.json index 1b5a18a57c0..39997d41bfe 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.addprreviewcomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.addprreviewcomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-checks-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.prfilecontents-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.prfilecontents-1.json index 65269a47b84..f31867024f8 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.prfilecontents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.prfilecontents-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-checks-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.rerunprchecks-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.rerunprchecks-1.json index 8c6cc35dac7..8243f2251d1 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.rerunprchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.rerunprchecks-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-checks-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.resolvereviewthread-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.resolvereviewthread-1.json index da6a63c6225..58f02e037bf 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.resolvereviewthread-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.resolvereviewthread-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-checks-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.setprfileviewed-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.setprfileviewed-1.json index eaa3d6d4b87..2c82f18a59f 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.setprfileviewed-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.setprfileviewed-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-checks-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-github-github.addissuecomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-github-github.addissuecomment-1.json index 83dd09774a4..9421433810b 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-github-github.addissuecomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-github-github.addissuecomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-comment-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-gitlab.addissuecomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-gitlab.addissuecomment-1.json index ea1968e8cbb..6d4ea7fe995 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-gitlab.addissuecomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-gitlab.addissuecomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-comment-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-mr-gitlab.addmrcomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-mr-gitlab.addmrcomment-1.json index f9d58525287..ed3c57b2d36 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-mr-gitlab.addmrcomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-mr-gitlab.addmrcomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-comment-gitlab-mr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-github-github.workitemdetails-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-github-github.workitemdetails-1.json index 03007d8522f..a2a086c6f22 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-github-github.workitemdetails-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-github-github.workitemdetails-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-gitlab-gitlab.workitemdetails-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-gitlab-gitlab.workitemdetails-1.json index 97fae23ab2a..9855cba3bd0 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-gitlab-gitlab.workitemdetails-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-gitlab-gitlab.workitemdetails-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.getissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.getissue-1.json index bdded9db611..702ff101be4 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.getissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.getissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.issuecomments-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.issuecomments-1.json index 1e705fe4cec..033943ccd1f 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.issuecomments-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.issuecomments-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listassignableusers-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listassignableusers-1.json index a67ada78a02..3d06fb953fc 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listassignableusers-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listassignableusers-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "58ea1553e04017c993aea4753aace41ee664705a3fdb3b18569c5a9d7968cf06", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listlabels-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listlabels-1.json index bbcc1202777..3a68b9f3621 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listlabels-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listlabels-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "58ea1553e04017c993aea4753aace41ee664705a3fdb3b18569c5a9d7968cf06", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-merge-gitlab-gitlab.mergemr-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-merge-gitlab-gitlab.mergemr-1.json index b5be03abfa4..cb12175440a 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-merge-gitlab-gitlab.mergemr-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-merge-gitlab-gitlab.mergemr-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-merge-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-github-github.updatepr-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-github-github.updatepr-1.json index 12c353ab736..68c00a9be32 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-github-github.updatepr-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-github-github.updatepr-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-metadata-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-gitlab.updateissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-gitlab.updateissue-1.json index 9997ac89918..3889eacdb3b 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-gitlab.updateissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-gitlab.updateissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-metadata-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-mr-gitlab.updatemr-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-mr-gitlab.updatemr-1.json index f7146e96a49..bfb4f34d7f5 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-mr-gitlab.updatemr-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-mr-gitlab.updatemr-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-metadata-gitlab-mr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addissuecomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addissuecomment-1.json index 6b1e2e889e2..fb6a16f6fd5 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addissuecomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addissuecomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-reply-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addprreviewcommentreply-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addprreviewcommentreply-1.json index 2ad97883d9d..a7075cb7037 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addprreviewcommentreply-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addprreviewcommentreply-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-reply-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.mergepr-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.mergepr-1.json index 297d62b6bf8..da413ea302d 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.mergepr-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.mergepr-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-reply-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-linear.updateissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-linear.updateissue-1.json index b20f92c54f3..648da246f0e 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-linear.updateissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-linear.updateissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-reply-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.prchecks-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.prchecks-1.json index 6c713ef0234..e6a66829415 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.prchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.prchecks-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-review-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.requestprreviewers-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.requestprreviewers-1.json index 20da53c66d4..f58b3f246fb 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.requestprreviewers-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.requestprreviewers-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-review-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-github.updateissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-github.updateissue-1.json index bf722b59b8f..01fa11bb598 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-github.updateissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-github.updateissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-status-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-gitlab.updateissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-gitlab.updateissue-1.json index 3292e750f9f..10c0ece2abb 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-gitlab.updateissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-gitlab.updateissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-status-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-mr-gitlab.updatemrstate-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-mr-gitlab.updatemrstate-1.json index 5237d617236..cd0751c12b7 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-mr-gitlab.updatemrstate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-mr-gitlab.updatemrstate-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-status-gitlab-mr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.linear-connect-linear.connect-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.linear-connect-linear.connect-1.json index 2a3c6b58fb5..382043a579e 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.linear-connect-linear.connect-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.linear-connect-linear.connect-1.json @@ -3,7 +3,7 @@ "family": "tasks.linear-connect", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.addissuecomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.addissuecomment-1.json index 3652d6ab0b7..7a0ca0436e7 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.addissuecomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.addissuecomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.linear-item", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.createissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.createissue-1.json index 7b8c01df292..6eea8946082 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.createissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.createissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.linear-item", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.getissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.getissue-1.json index a08c323c624..e9070dfae3f 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.getissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.getissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.linear-item", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.listteams-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.listteams-1.json index 50c3d200468..f861bef99b1 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.listteams-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.listteams-1.json @@ -3,7 +3,7 @@ "family": "tasks.linear-team-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "58ea1553e04017c993aea4753aace41ee664705a3fdb3b18569c5a9d7968cf06", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.teamstates-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.teamstates-1.json index 46bcc3cd225..505b6397aca 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.teamstates-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.teamstates-1.json @@ -3,7 +3,7 @@ "family": "tasks.linear-team-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "58ea1553e04017c993aea4753aace41ee664705a3fdb3b18569c5a9d7968cf06", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.json index 63efaca3acd..741d7300ad5 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.json index cd8589b0243..e2b0a492671 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.json index 23fccbb0d23..306c8da4799 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.json index 713a65f8e21..06510a4ba13 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listaccessible-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listaccessible-1.json index 761c9022351..b0083056f35 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listaccessible-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listaccessible-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-board-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-1.json index 38086946480..5f6a9115b33 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-board-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-2.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-2.json index 0be2ce938b4..1cb2a7f8e1f 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-2.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-2.json @@ -3,7 +3,7 @@ "family": "tasks.project-board-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.resolveref-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.resolveref-1.json index e5188852085..724480e2513 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.resolveref-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.resolveref-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-board-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.viewtable-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.viewtable-1.json index 6dfb3ebf8f0..6c4de159aa3 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.viewtable-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.viewtable-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-board-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-repo-slugs-github.reposlug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-repo-slugs-github.reposlug-1.json index 165b8e0ac2c..989df660c39 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-repo-slugs-github.reposlug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-repo-slugs-github.reposlug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-repo-slugs", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.addissuecommentbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.addissuecommentbyslug-1.json index f776213bf2c..e1ac164e4c6 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.addissuecommentbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.addissuecommentbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-comments-issue", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuebyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuebyslug-1.json index 5d4cb5623ad..aaedb2b6a00 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuebyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuebyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-comments-issue", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuecommentbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuecommentbyslug-1.json index 7084b8b607f..de82cc03bac 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuecommentbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuecommentbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-comments-issue", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-pr-github.project.updatepullrequestbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-pr-github.project.updatepullrequestbyslug-1.json index 1570e40effa..d538a1885c4 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-pr-github.project.updatepullrequestbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-pr-github.project.updatepullrequestbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-comments-pr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-detail-github.project.workitemdetailsbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-detail-github.project.workitemdetailsbyslug-1.json index 1389d17f632..80ac3991978 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-detail-github.project.workitemdetailsbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-detail-github.project.workitemdetailsbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-detail", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f8f6e5d500f959b9b15c5498885a05422747880b6aef4ad795bc3064ebbacea6", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.clearitemfield-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.clearitemfield-1.json index f51ad0b39c8..c4a7cc1ae22 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.clearitemfield-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.clearitemfield-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-fields", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateissuetypebyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateissuetypebyslug-1.json index 25cbb86afb6..3086b491114 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateissuetypebyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateissuetypebyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-fields", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateitemfield-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateitemfield-1.json index 598b31e2c35..f451f24d47e 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateitemfield-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateitemfield-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-fields", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.addprreviewcomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.addprreviewcomment-1.json index 8a5d618b70e..b47246133b6 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.addprreviewcomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.addprreviewcomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-files-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b228732762828412ad3d9eec3ece00a897d866046e37044322c3911758d6e0a9", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.mergepr-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.mergepr-1.json index b1ce1595bdd..efd18b1fe60 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.mergepr-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.mergepr-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-files-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b228732762828412ad3d9eec3ece00a897d866046e37044322c3911758d6e0a9", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.prfilecontents-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.prfilecontents-1.json index 6e0bdcbb4bb..cbef12adfa2 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.prfilecontents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.prfilecontents-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-files-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b228732762828412ad3d9eec3ece00a897d866046e37044322c3911758d6e0a9", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateissue-1.json index 9bd02baa925..96530b21a0c 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-files-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b228732762828412ad3d9eec3ece00a897d866046e37044322c3911758d6e0a9", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateprstate-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateprstate-1.json index 126b4d22b89..37b12396039 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateprstate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateprstate-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-files-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b228732762828412ad3d9eec3ece00a897d866046e37044322c3911758d6e0a9", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listassignableusersbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listassignableusersbyslug-1.json index 31483b680e0..59486a1564a 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listassignableusersbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listassignableusersbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-metadata-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f8f6e5d500f959b9b15c5498885a05422747880b6aef4ad795bc3064ebbacea6", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listissuetypesbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listissuetypesbyslug-1.json index 7152a57420b..03fdd96ab2a 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listissuetypesbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listissuetypesbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-metadata-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f8f6e5d500f959b9b15c5498885a05422747880b6aef4ad795bc3064ebbacea6", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listlabelsbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listlabelsbyslug-1.json index b873a7a9270..47d90225433 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listlabelsbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listlabelsbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-metadata-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f8f6e5d500f959b9b15c5498885a05422747880b6aef4ad795bc3064ebbacea6", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.prchecks-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.prchecks-1.json index 7cf7224b191..9d0490d2a99 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.prchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.prchecks-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-review-checks", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.requestprreviewers-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.requestprreviewers-1.json index 938a223e4c4..32568e1064e 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.requestprreviewers-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.requestprreviewers-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-review-checks", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.rerunprchecks-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.rerunprchecks-1.json index cf6f8b13a09..cdedfced984 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.rerunprchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.rerunprchecks-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-review-checks", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.setprfileviewed-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.setprfileviewed-1.json index d1caf976991..aeef728df1d 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.setprfileviewed-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.setprfileviewed-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-review-checks", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addissuecomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addissuecomment-1.json index 96eaf154133..7eedede0ae5 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addissuecomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addissuecomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-threads", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addprreviewcommentreply-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addprreviewcommentreply-1.json index 68c2697ee2f..387a5c3ef2c 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addprreviewcommentreply-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addprreviewcommentreply-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-threads", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.project.deleteissuecommentbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.project.deleteissuecommentbyslug-1.json index 8c4339876d6..4554867bbab 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.project.deleteissuecommentbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.project.deleteissuecommentbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-threads", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.resolvereviewthread-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.resolvereviewthread-1.json index 8c37970c562..9fba9c25c56 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.resolvereviewthread-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.resolvereviewthread-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-threads", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.countworkitems-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.countworkitems-1.json index fe1ee8c81e5..8faf0a86208 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.countworkitems-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.countworkitems-1.json @@ -3,7 +3,7 @@ "family": "tasks.provider-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.listworkitems-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.listworkitems-1.json index db67083f530..c5196794afc 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.listworkitems-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.listworkitems-1.json @@ -3,7 +3,7 @@ "family": "tasks.provider-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.listteams-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.listteams-1.json index 0560b7a88d9..659ba94f39a 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.listteams-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.listteams-1.json @@ -3,7 +3,7 @@ "family": "tasks.provider-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.status-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.status-1.json index ec5129a1e4d..ffb36d12a77 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.status-1.json @@ -3,7 +3,7 @@ "family": "tasks.provider-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-settings.update-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-settings.update-1.json index 54f2ca95fdc..b4fb1f682c1 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-settings.update-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-settings.update-1.json @@ -3,7 +3,7 @@ "family": "tasks.provider-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.route-repo-list-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.route-repo-list-repo.list-1.json index 14d2f60761e..e83bf010410 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.route-repo-list-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.route-repo-list-repo.list-1.json @@ -3,7 +3,7 @@ "family": "tasks.route-repo-list", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "feb6cee1ab7ecff1ba98bfba22d4924c748d3bb6b749db460cb617ee50b92f2c", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.json index 4a509290d2e..f9b17812895 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.json index 72ef7fd00d4..4d83e4407bf 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json index 380e6b68c36..e7a9b74fedb 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.json index 83278ef0931..6b191290cb7 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.json index f268bd20f2b..bee1f7226cf 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-github.createissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-github.createissue-1.json index cc193fea1f5..f345a3fc18c 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-github.createissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-github.createissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-repo.update-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-repo.update-1.json index 2239020ada0..8c3de55e342 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-repo.update-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-repo.update-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-gitlab-gitlab.createissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-gitlab-gitlab.createissue-1.json index 944a0549a6c..21fb2acd90d 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-gitlab-gitlab.createissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-gitlab-gitlab.createissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-linear-linear.createissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-linear-linear.createissue-1.json index f6239258356..d228fb497fb 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-linear-linear.createissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-linear-linear.createissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-items-gitlab.listworkitems-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-items-gitlab.listworkitems-1.json index 3dba064bb1c..5e8c187a1d2 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-items-gitlab.listworkitems-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-items-gitlab.listworkitems-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-gitlab-items", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-todos-gitlab.todos-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-todos-gitlab.todos-1.json index 79b8c640b7f..34b80e99406 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-todos-gitlab.todos-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-todos-gitlab.todos-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-gitlab-todos", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.listissues-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.listissues-1.json index 8333ce3087b..604bdb34d8b 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.listissues-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.listissues-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.searchissues-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.searchissues-1.json index b1269ed0dc8..b8f57d7287e 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.searchissues-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.searchissues-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json index 0697b535d5f..ed50193690d 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-source", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.json index 5e24692439d..6066e0dc301 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-source", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json index dcc6a45bef0..c7864af77c7 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-sparse", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json index 79fed1caff1..00ff849e309 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-sparse", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.json index 8342b88f71e..8bc23761fd1 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh-local", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.json index 208942fe11f..918d659d2d4 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.json index c50d43b820a..ababdf06fd2 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json index ad89f54f4c8..21d92d28e5d 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-terminal.query-reply-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-terminal.query-reply-terminal.send-1.json index 6b1547b83a8..50eab746bb5 100644 --- a/mobile/rpc-foundation/goldens/matrix-terminal.query-reply-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-terminal.query-reply-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "terminal.query-reply", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-orchestration.workerterminaluserinput-1.json index 000afa2806b..756931e6580 100644 --- a/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "terminal.raw-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-terminal.send-1.json index d7b90cb740d..e06766b3d87 100644 --- a/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "terminal.raw-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-1.json index f98eb119785..06753b20b22 100644 --- a/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "terminal.takeover-report", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-2.json b/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-2.json index 0c3dd0f6a07..e1a427cac8e 100644 --- a/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-2.json +++ b/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-2.json @@ -3,7 +3,7 @@ "family": "terminal.takeover-report", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/matrix-terminal.viewport-refit-terminal.updateviewport-1.json b/mobile/rpc-foundation/goldens/matrix-terminal.viewport-refit-terminal.updateviewport-1.json index db6e4063096..7a6918ad255 100644 --- a/mobile/rpc-foundation/goldens/matrix-terminal.viewport-refit-terminal.updateviewport-1.json +++ b/mobile/rpc-foundation/goldens/matrix-terminal.viewport-refit-terminal.updateviewport-1.json @@ -3,7 +3,7 @@ "family": "terminal.viewport-refit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.json index f6801709469..3c9a933656e 100644 --- a/mobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.json @@ -3,7 +3,7 @@ "family": "transport.capability-probe", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.json index f0b59591ffc..3a2356fc7d3 100644 --- a/mobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.json @@ -3,7 +3,7 @@ "family": "transport.host-status-gates", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.json b/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.json index 4788382dfe1..c19b1042087 100644 --- a/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.json +++ b/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.json @@ -3,7 +3,7 @@ "family": "transport.pairing-race", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.json b/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.json index e18ba900051..488d081c6ac 100644 --- a/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.json +++ b/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.json @@ -3,7 +3,7 @@ "family": "transport.pairing-race", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.agent-launch-create-agent.launch-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.agent-launch-create-agent.launch-1.json index 547db031a77..b62925fdcb5 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.agent-launch-create-agent.launch-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.agent-launch-create-agent.launch-1.json @@ -3,7 +3,7 @@ "family": "worktree.agent-launch-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.catalog-snapshot-worktree.ps-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.catalog-snapshot-worktree.ps-1.json index 74dcffc35a1..fdeab28aa9d 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.catalog-snapshot-worktree.ps-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.catalog-snapshot-worktree.ps-1.json @@ -3,7 +3,7 @@ "family": "worktree.catalog-snapshot", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json index 2a1cbabab1a..acaeb2b32e9 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.home-catalog-worktree.ps-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.home-catalog-worktree.ps-1.json index cb0b0624633..dd6ce21a045 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.home-catalog-worktree.ps-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.home-catalog-worktree.ps-1.json @@ -3,7 +3,7 @@ "family": "worktree.home-catalog", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.json index 1f51bc8ee3e..abcbee1d756 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.json @@ -3,7 +3,7 @@ "family": "worktree.hosted-base", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.json index d7453f577ac..57aafc46bb1 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.json @@ -3,7 +3,7 @@ "family": "worktree.hosted-base", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.retired-names-worktree.listretirednames-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.retired-names-worktree.listretirednames-1.json index baaa2b016f4..9861f3f313a 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.retired-names-worktree.listretirednames-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.retired-names-worktree.listretirednames-1.json @@ -3,7 +3,7 @@ "family": "worktree.retired-names", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.json index 5bf1ae4bf2f..ab4dfda570a 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.json @@ -3,7 +3,7 @@ "family": "worktree.review-link", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.json index 6d99ad29779..c270946be74 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.json @@ -3,7 +3,7 @@ "family": "worktree.runtime-capabilities", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.json index ba6d6503b54..011e86777de 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.json @@ -3,7 +3,7 @@ "family": "worktree.setup-hook-trust", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-paste-single.json b/mobile/rpc-foundation/goldens/native-chat-image-paste-single.json index d57d8a35296..2b887a29805 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-paste-single.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-paste-single.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-paste-stops-on-rejection.json b/mobile/rpc-foundation/goldens/native-chat-image-paste-stops-on-rejection.json index e3f85ba2b40..a1cf7db96e5 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-paste-stops-on-rejection.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-paste-stops-on-rejection.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-paste-trailing-image.json b/mobile/rpc-foundation/goldens/native-chat-image-paste-trailing-image.json index 75ec05f3ba0..fea9ab22020 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-paste-trailing-image.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-paste-trailing-image.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-paste-two-images.json b/mobile/rpc-foundation/goldens/native-chat-image-paste-two-images.json index b6b24213264..791e6a62923 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-paste-two-images.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-paste-two-images.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-upload-cancelled.json b/mobile/rpc-foundation/goldens/native-chat-image-upload-cancelled.json index 68053fbf1e9..3fc3c75d837 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-upload-cancelled.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-upload-cancelled.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-upload-second-fails.json b/mobile/rpc-foundation/goldens/native-chat-image-upload-second-fails.json index da8ad44da02..4ea8a3db5d4 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-upload-second-fails.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-upload-second-fails.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-upload-single.json b/mobile/rpc-foundation/goldens/native-chat-image-upload-single.json index 721c41f9d03..edcded11ee3 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-upload-single.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-upload-single.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-upload-start-refused.json b/mobile/rpc-foundation/goldens/native-chat-image-upload-start-refused.json index e3304ffc1df..9b9eaf4e689 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-upload-start-refused.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-upload-start-refused.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-upload-two.json b/mobile/rpc-foundation/goldens/native-chat-image-upload-two.json index e5b8734a885..64e6ca77092 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-upload-two.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-upload-two.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-page-earlier.json b/mobile/rpc-foundation/goldens/native-chat-page-earlier.json index 101f89e087c..66bf225b507 100644 --- a/mobile/rpc-foundation/goldens/native-chat-page-earlier.json +++ b/mobile/rpc-foundation/goldens/native-chat-page-earlier.json @@ -3,7 +3,7 @@ "family": "session.native-chat-page", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "60ce67f66134d6385aa7fb47f2135436a7b3ce0221e3b2514bea65e06dba5518", diff --git a/mobile/rpc-foundation/goldens/native-chat-readability-local-repo.json b/mobile/rpc-foundation/goldens/native-chat-readability-local-repo.json index 3684c7d18c2..50495ff505e 100644 --- a/mobile/rpc-foundation/goldens/native-chat-readability-local-repo.json +++ b/mobile/rpc-foundation/goldens/native-chat-readability-local-repo.json @@ -3,7 +3,7 @@ "family": "session.native-chat-readability", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/native-chat-readability-refused.json b/mobile/rpc-foundation/goldens/native-chat-readability-refused.json index b3ddc056eed..85f12f68a49 100644 --- a/mobile/rpc-foundation/goldens/native-chat-readability-refused.json +++ b/mobile/rpc-foundation/goldens/native-chat-readability-refused.json @@ -3,7 +3,7 @@ "family": "session.native-chat-readability", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/native-chat-readability-remote-repo.json b/mobile/rpc-foundation/goldens/native-chat-readability-remote-repo.json index 75b08605a0f..dbc8023cb1b 100644 --- a/mobile/rpc-foundation/goldens/native-chat-readability-remote-repo.json +++ b/mobile/rpc-foundation/goldens/native-chat-readability-remote-repo.json @@ -3,7 +3,7 @@ "family": "session.native-chat-readability", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/native-chat-session-option-pick-empty.json b/mobile/rpc-foundation/goldens/native-chat-session-option-pick-empty.json index a1533cf44d8..e0c555ecbca 100644 --- a/mobile/rpc-foundation/goldens/native-chat-session-option-pick-empty.json +++ b/mobile/rpc-foundation/goldens/native-chat-session-option-pick-empty.json @@ -3,7 +3,7 @@ "family": "nativeChat.session-option-pick", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-session-option-pick-refused.json b/mobile/rpc-foundation/goldens/native-chat-session-option-pick-refused.json index e8af4d033b5..6499355e828 100644 --- a/mobile/rpc-foundation/goldens/native-chat-session-option-pick-refused.json +++ b/mobile/rpc-foundation/goldens/native-chat-session-option-pick-refused.json @@ -3,7 +3,7 @@ "family": "nativeChat.session-option-pick", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-session-option-pick-written.json b/mobile/rpc-foundation/goldens/native-chat-session-option-pick-written.json index 24cb2bbfb5b..6d0018d3fe2 100644 --- a/mobile/rpc-foundation/goldens/native-chat-session-option-pick-written.json +++ b/mobile/rpc-foundation/goldens/native-chat-session-option-pick-written.json @@ -3,7 +3,7 @@ "family": "nativeChat.session-option-pick", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-stop-accepted.json b/mobile/rpc-foundation/goldens/native-chat-stop-accepted.json index dd0474ef3ad..f89e1a1e346 100644 --- a/mobile/rpc-foundation/goldens/native-chat-stop-accepted.json +++ b/mobile/rpc-foundation/goldens/native-chat-stop-accepted.json @@ -3,7 +3,7 @@ "family": "session.native-chat-stop", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/native-chat-stop-both-rejected.json b/mobile/rpc-foundation/goldens/native-chat-stop-both-rejected.json index c5035e941da..2dd1036dcd0 100644 --- a/mobile/rpc-foundation/goldens/native-chat-stop-both-rejected.json +++ b/mobile/rpc-foundation/goldens/native-chat-stop-both-rejected.json @@ -3,7 +3,7 @@ "family": "session.native-chat-stop", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/native-chat-stop-delivery-unknown.json b/mobile/rpc-foundation/goldens/native-chat-stop-delivery-unknown.json index e84ce07ca3a..774fd467bbb 100644 --- a/mobile/rpc-foundation/goldens/native-chat-stop-delivery-unknown.json +++ b/mobile/rpc-foundation/goldens/native-chat-stop-delivery-unknown.json @@ -3,7 +3,7 @@ "family": "session.native-chat-stop", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/native-chat-write-accepted.json b/mobile/rpc-foundation/goldens/native-chat-write-accepted.json index b5878d28cd1..99affcf63dd 100644 --- a/mobile/rpc-foundation/goldens/native-chat-write-accepted.json +++ b/mobile/rpc-foundation/goldens/native-chat-write-accepted.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-write-clear-line.json b/mobile/rpc-foundation/goldens/native-chat-write-clear-line.json index 3c7e4e45d16..6f0ed593047 100644 --- a/mobile/rpc-foundation/goldens/native-chat-write-clear-line.json +++ b/mobile/rpc-foundation/goldens/native-chat-write-clear-line.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-write-delivery-unknown.json b/mobile/rpc-foundation/goldens/native-chat-write-delivery-unknown.json index 5c57d1ee792..b7294b083ca 100644 --- a/mobile/rpc-foundation/goldens/native-chat-write-delivery-unknown.json +++ b/mobile/rpc-foundation/goldens/native-chat-write-delivery-unknown.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-write-rejected.json b/mobile/rpc-foundation/goldens/native-chat-write-rejected.json index 105c374abc2..9abaeb2b2a8 100644 --- a/mobile/rpc-foundation/goldens/native-chat-write-rejected.json +++ b/mobile/rpc-foundation/goldens/native-chat-write-rejected.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-write-typed-command.json b/mobile/rpc-foundation/goldens/native-chat-write-typed-command.json index 74998fe3cce..8ef29a4af7c 100644 --- a/mobile/rpc-foundation/goldens/native-chat-write-typed-command.json +++ b/mobile/rpc-foundation/goldens/native-chat-write-typed-command.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/new-tab-local-agents.json b/mobile/rpc-foundation/goldens/new-tab-local-agents.json index 3bd6e796e72..816c44d143a 100644 --- a/mobile/rpc-foundation/goldens/new-tab-local-agents.json +++ b/mobile/rpc-foundation/goldens/new-tab-local-agents.json @@ -3,7 +3,7 @@ "family": "settings.new-tab-local-agents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/new-workspace-repositories-fulfilled.json b/mobile/rpc-foundation/goldens/new-workspace-repositories-fulfilled.json index 7b82dbebb6c..216623e6280 100644 --- a/mobile/rpc-foundation/goldens/new-workspace-repositories-fulfilled.json +++ b/mobile/rpc-foundation/goldens/new-workspace-repositories-fulfilled.json @@ -3,7 +3,7 @@ "family": "components.new-workspace-repositories", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "64c1772f0f95a3c43fbb14398a8804b2b4784f7f18874e4fd79767ae634c7faa", diff --git a/mobile/rpc-foundation/goldens/notifications-desktop-stream-closed.json b/mobile/rpc-foundation/goldens/notifications-desktop-stream-closed.json index 340afbd799b..d27391e7558 100644 --- a/mobile/rpc-foundation/goldens/notifications-desktop-stream-closed.json +++ b/mobile/rpc-foundation/goldens/notifications-desktop-stream-closed.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/notifications-desktop-stream-replayed.json b/mobile/rpc-foundation/goldens/notifications-desktop-stream-replayed.json index 03067bafad6..2596b9aa6a2 100644 --- a/mobile/rpc-foundation/goldens/notifications-desktop-stream-replayed.json +++ b/mobile/rpc-foundation/goldens/notifications-desktop-stream-replayed.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/notifications-desktop-stream.json b/mobile/rpc-foundation/goldens/notifications-desktop-stream.json index 3e9f6f6e105..4404ce96fce 100644 --- a/mobile/rpc-foundation/goldens/notifications-desktop-stream.json +++ b/mobile/rpc-foundation/goldens/notifications-desktop-stream.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/notifications-display-test-accepted.json b/mobile/rpc-foundation/goldens/notifications-display-test-accepted.json index 4126a883af4..f9b3572b0cf 100644 --- a/mobile/rpc-foundation/goldens/notifications-display-test-accepted.json +++ b/mobile/rpc-foundation/goldens/notifications-display-test-accepted.json @@ -3,7 +3,7 @@ "family": "notifications.display-test-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "12a04986290715d5db23a1eb5192c1138bb2d375d1b1dd37ba310ea89eb11566", diff --git a/mobile/rpc-foundation/goldens/notifications-display-test-not-registered.json b/mobile/rpc-foundation/goldens/notifications-display-test-not-registered.json index 0f860f17f7d..dcf9ed6c388 100644 --- a/mobile/rpc-foundation/goldens/notifications-display-test-not-registered.json +++ b/mobile/rpc-foundation/goldens/notifications-display-test-not-registered.json @@ -3,7 +3,7 @@ "family": "notifications.display-test-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "12a04986290715d5db23a1eb5192c1138bb2d375d1b1dd37ba310ea89eb11566", diff --git a/mobile/rpc-foundation/goldens/notifications-display-test-rate-limited.json b/mobile/rpc-foundation/goldens/notifications-display-test-rate-limited.json index d3a4ad64ce8..40058fe7310 100644 --- a/mobile/rpc-foundation/goldens/notifications-display-test-rate-limited.json +++ b/mobile/rpc-foundation/goldens/notifications-display-test-rate-limited.json @@ -3,7 +3,7 @@ "family": "notifications.display-test-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "12a04986290715d5db23a1eb5192c1138bb2d375d1b1dd37ba310ea89eb11566", diff --git a/mobile/rpc-foundation/goldens/notifications-display-test-unknown-reason.json b/mobile/rpc-foundation/goldens/notifications-display-test-unknown-reason.json index 1cb9fb00c63..8f4120ebf75 100644 --- a/mobile/rpc-foundation/goldens/notifications-display-test-unknown-reason.json +++ b/mobile/rpc-foundation/goldens/notifications-display-test-unknown-reason.json @@ -3,7 +3,7 @@ "family": "notifications.display-test-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "12a04986290715d5db23a1eb5192c1138bb2d375d1b1dd37ba310ea89eb11566", diff --git a/mobile/rpc-foundation/goldens/notifications-push-gateway-rejected.json b/mobile/rpc-foundation/goldens/notifications-push-gateway-rejected.json index 7ce35fa060e..42e95ac2281 100644 --- a/mobile/rpc-foundation/goldens/notifications-push-gateway-rejected.json +++ b/mobile/rpc-foundation/goldens/notifications-push-gateway-rejected.json @@ -3,7 +3,7 @@ "family": "notifications.push-registration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2e3d939dc162dbc5a38d8a7207111688204a825fd70348721917b3016e1c9470", diff --git a/mobile/rpc-foundation/goldens/notifications-push-registered.json b/mobile/rpc-foundation/goldens/notifications-push-registered.json index 7884d2d5fdd..020d3df8cbb 100644 --- a/mobile/rpc-foundation/goldens/notifications-push-registered.json +++ b/mobile/rpc-foundation/goldens/notifications-push-registered.json @@ -3,7 +3,7 @@ "family": "notifications.push-registration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2e3d939dc162dbc5a38d8a7207111688204a825fd70348721917b3016e1c9470", diff --git a/mobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.json b/mobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.json index 2f9d92dcd9f..4f4e22c10c1 100644 --- a/mobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.json +++ b/mobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.json b/mobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.json index e8233126f72..3b85ea1e8fe 100644 --- a/mobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.json +++ b/mobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/pairing-pre-profile-times-out.json b/mobile/rpc-foundation/goldens/pairing-pre-profile-times-out.json index bbbeef88765..3864c539c18 100644 --- a/mobile/rpc-foundation/goldens/pairing-pre-profile-times-out.json +++ b/mobile/rpc-foundation/goldens/pairing-pre-profile-times-out.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/pr-branch-identity.json b/mobile/rpc-foundation/goldens/pr-branch-identity.json index ca79995884f..b0b734b181f 100644 --- a/mobile/rpc-foundation/goldens/pr-branch-identity.json +++ b/mobile/rpc-foundation/goldens/pr-branch-identity.json @@ -3,7 +3,7 @@ "family": "session.pr-branch-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-branch-repo-context.json b/mobile/rpc-foundation/goldens/pr-branch-repo-context.json index 86b350a2faa..43898e1e240 100644 --- a/mobile/rpc-foundation/goldens/pr-branch-repo-context.json +++ b/mobile/rpc-foundation/goldens/pr-branch-repo-context.json @@ -3,7 +3,7 @@ "family": "session.pr-branch-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-comment-mutation.json b/mobile/rpc-foundation/goldens/pr-comment-mutation.json index 3219211e1b7..8f56f34d033 100644 --- a/mobile/rpc-foundation/goldens/pr-comment-mutation.json +++ b/mobile/rpc-foundation/goldens/pr-comment-mutation.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-comment-resolve-unconfirmed.json b/mobile/rpc-foundation/goldens/pr-comment-resolve-unconfirmed.json index a714e866c5c..4a7b2a03575 100644 --- a/mobile/rpc-foundation/goldens/pr-comment-resolve-unconfirmed.json +++ b/mobile/rpc-foundation/goldens/pr-comment-resolve-unconfirmed.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-mutation-in-band-failure.json b/mobile/rpc-foundation/goldens/pr-mutation-in-band-failure.json index eb1f87a9ebf..6a2a6a0b6b3 100644 --- a/mobile/rpc-foundation/goldens/pr-mutation-in-band-failure.json +++ b/mobile/rpc-foundation/goldens/pr-mutation-in-band-failure.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-mutation-status.json b/mobile/rpc-foundation/goldens/pr-mutation-status.json index 0cd01c52126..8f4870b8652 100644 --- a/mobile/rpc-foundation/goldens/pr-mutation-status.json +++ b/mobile/rpc-foundation/goldens/pr-mutation-status.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-read-fork-routing.json b/mobile/rpc-foundation/goldens/pr-read-fork-routing.json index 0313c5fc917..8a51dfd96a8 100644 --- a/mobile/rpc-foundation/goldens/pr-read-fork-routing.json +++ b/mobile/rpc-foundation/goldens/pr-read-fork-routing.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-read-surface.json b/mobile/rpc-foundation/goldens/pr-read-surface.json index ebe53958ae6..fd2bf46bf60 100644 --- a/mobile/rpc-foundation/goldens/pr-read-surface.json +++ b/mobile/rpc-foundation/goldens/pr-read-surface.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-read-upstream-error.json b/mobile/rpc-foundation/goldens/pr-read-upstream-error.json index 1b7c56235a0..2fce3409f87 100644 --- a/mobile/rpc-foundation/goldens/pr-read-upstream-error.json +++ b/mobile/rpc-foundation/goldens/pr-read-upstream-error.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-sidebar-checks-refused.json b/mobile/rpc-foundation/goldens/pr-sidebar-checks-refused.json index 5daa0cf3ad6..9d2a21e3bf6 100644 --- a/mobile/rpc-foundation/goldens/pr-sidebar-checks-refused.json +++ b/mobile/rpc-foundation/goldens/pr-sidebar-checks-refused.json @@ -3,7 +3,7 @@ "family": "session.pr-sidebar", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "87ffa2daea415d2682f1112025b5bd9d52404c6fc7c239180a3ad2120678ff1f", diff --git a/mobile/rpc-foundation/goldens/pr-sidebar-load.json b/mobile/rpc-foundation/goldens/pr-sidebar-load.json index 426056b00bf..e96149d9f02 100644 --- a/mobile/rpc-foundation/goldens/pr-sidebar-load.json +++ b/mobile/rpc-foundation/goldens/pr-sidebar-load.json @@ -3,7 +3,7 @@ "family": "session.pr-sidebar", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "87ffa2daea415d2682f1112025b5bd9d52404c6fc7c239180a3ad2120678ff1f", diff --git a/mobile/rpc-foundation/goldens/pr-title-mutation.json b/mobile/rpc-foundation/goldens/pr-title-mutation.json index 5e1e60edfdf..1b9270611e7 100644 --- a/mobile/rpc-foundation/goldens/pr-title-mutation.json +++ b/mobile/rpc-foundation/goldens/pr-title-mutation.json @@ -3,7 +3,7 @@ "family": "github.pr-title-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-title-unconfirmed.json b/mobile/rpc-foundation/goldens/pr-title-unconfirmed.json index fdb63408d7c..2bd409127fd 100644 --- a/mobile/rpc-foundation/goldens/pr-title-unconfirmed.json +++ b/mobile/rpc-foundation/goldens/pr-title-unconfirmed.json @@ -3,7 +3,7 @@ "family": "github.pr-title-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-triage-invalid-terminal.json b/mobile/rpc-foundation/goldens/pr-triage-invalid-terminal.json index 0e04e71edf3..7ae4ce93a97 100644 --- a/mobile/rpc-foundation/goldens/pr-triage-invalid-terminal.json +++ b/mobile/rpc-foundation/goldens/pr-triage-invalid-terminal.json @@ -3,7 +3,7 @@ "family": "session.pr-triage", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-triage-launch.json b/mobile/rpc-foundation/goldens/pr-triage-launch.json index 2b83758fb98..6a878439dcf 100644 --- a/mobile/rpc-foundation/goldens/pr-triage-launch.json +++ b/mobile/rpc-foundation/goldens/pr-triage-launch.json @@ -3,7 +3,7 @@ "family": "session.pr-triage", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-triage-send-locked.json b/mobile/rpc-foundation/goldens/pr-triage-send-locked.json index 1c86d532927..76c10459225 100644 --- a/mobile/rpc-foundation/goldens/pr-triage-send-locked.json +++ b/mobile/rpc-foundation/goldens/pr-triage-send-locked.json @@ -3,7 +3,7 @@ "family": "session.pr-triage", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json b/mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json index 65482972dcf..d8fc583d638 100644 --- a/mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json +++ b/mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json b/mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json index 19d5ff71b7a..40864e8f670 100644 --- a/mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json +++ b/mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.json b/mobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.json index 640cd50b1fb..2b0c07b066c 100644 --- a/mobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.json +++ b/mobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json b/mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json index f8ce7ad235e..ac211c53766 100644 --- a/mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json +++ b/mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/push-dismissal-tray-reconciled.json b/mobile/rpc-foundation/goldens/push-dismissal-tray-reconciled.json index 0c80acc02b8..87f4180c5f8 100644 --- a/mobile/rpc-foundation/goldens/push-dismissal-tray-reconciled.json +++ b/mobile/rpc-foundation/goldens/push-dismissal-tray-reconciled.json @@ -3,7 +3,7 @@ "family": "notifications.push-dismissal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "595a3eb2994d0596b9fcd0707b175b4e978625053dfbc5c541b0350c0cbfb524", diff --git a/mobile/rpc-foundation/goldens/quick-commands-load-refused.json b/mobile/rpc-foundation/goldens/quick-commands-load-refused.json index 4ac464e3289..b3be8010e88 100644 --- a/mobile/rpc-foundation/goldens/quick-commands-load-refused.json +++ b/mobile/rpc-foundation/goldens/quick-commands-load-refused.json @@ -3,7 +3,7 @@ "family": "settings.quick-commands", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/quick-commands-loaded-and-saved.json b/mobile/rpc-foundation/goldens/quick-commands-loaded-and-saved.json index 806471d0b2f..fd4d8f61aeb 100644 --- a/mobile/rpc-foundation/goldens/quick-commands-loaded-and-saved.json +++ b/mobile/rpc-foundation/goldens/quick-commands-loaded-and-saved.json @@ -3,7 +3,7 @@ "family": "settings.quick-commands", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/quick-commands-save-refused-rolls-back.json b/mobile/rpc-foundation/goldens/quick-commands-save-refused-rolls-back.json index 857859ca472..d7fce4d9474 100644 --- a/mobile/rpc-foundation/goldens/quick-commands-save-refused-rolls-back.json +++ b/mobile/rpc-foundation/goldens/quick-commands-save-refused-rolls-back.json @@ -3,7 +3,7 @@ "family": "settings.quick-commands", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/relay-direct-upgrade-commits.json b/mobile/rpc-foundation/goldens/relay-direct-upgrade-commits.json index a81f114b7d1..905172421fa 100644 --- a/mobile/rpc-foundation/goldens/relay-direct-upgrade-commits.json +++ b/mobile/rpc-foundation/goldens/relay-direct-upgrade-commits.json @@ -3,7 +3,7 @@ "family": "relay.direct-upgrade", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.json b/mobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.json index e03bd56fcc0..1d22fd580bf 100644 --- a/mobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.json +++ b/mobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.json @@ -3,7 +3,7 @@ "family": "relay.direct-upgrade", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.json b/mobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.json index fb6d02ff8fc..3392e86f47c 100644 --- a/mobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.json +++ b/mobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.json @@ -3,7 +3,7 @@ "family": "relay.pairing-recovery", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.json b/mobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.json index bc8c28434b9..9e81dfaebcc 100644 --- a/mobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.json +++ b/mobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.json @@ -3,7 +3,7 @@ "family": "relay.pairing-recovery", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.json b/mobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.json index bb5f2b3eabc..ca4c8a9a7fb 100644 --- a/mobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.json +++ b/mobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.json @@ -3,7 +3,7 @@ "family": "relay.credential-rotation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.json b/mobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.json index 3d8dd2b38fd..cdbae2d9d21 100644 --- a/mobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.json +++ b/mobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.json @@ -3,7 +3,7 @@ "family": "relay.credential-rotation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/review-branch-diff-shapes.json b/mobile/rpc-foundation/goldens/review-branch-diff-shapes.json index a06c646de80..48a5b39e395 100644 --- a/mobile/rpc-foundation/goldens/review-branch-diff-shapes.json +++ b/mobile/rpc-foundation/goldens/review-branch-diff-shapes.json @@ -3,7 +3,7 @@ "family": "session.review-branch-diff", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/review-create-terminal-refused.json b/mobile/rpc-foundation/goldens/review-create-terminal-refused.json index 46828f345dd..9302dac0b6f 100644 --- a/mobile/rpc-foundation/goldens/review-create-terminal-refused.json +++ b/mobile/rpc-foundation/goldens/review-create-terminal-refused.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-file-diff-shapes.json b/mobile/rpc-foundation/goldens/review-file-diff-shapes.json index 65e52c63ec1..11b479f7e3f 100644 --- a/mobile/rpc-foundation/goldens/review-file-diff-shapes.json +++ b/mobile/rpc-foundation/goldens/review-file-diff-shapes.json @@ -3,7 +3,7 @@ "family": "session.review-file-diff", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/review-git-mutations-run.json b/mobile/rpc-foundation/goldens/review-git-mutations-run.json index f5fb98b467d..705b772d82b 100644 --- a/mobile/rpc-foundation/goldens/review-git-mutations-run.json +++ b/mobile/rpc-foundation/goldens/review-git-mutations-run.json @@ -3,7 +3,7 @@ "family": "session.review-git-mutations", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-mark-reviewed-persists.json b/mobile/rpc-foundation/goldens/review-mark-reviewed-persists.json index 6a7746ef50e..9b19a79319f 100644 --- a/mobile/rpc-foundation/goldens/review-mark-reviewed-persists.json +++ b/mobile/rpc-foundation/goldens/review-mark-reviewed-persists.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-mark-reviewed-rolls-back.json b/mobile/rpc-foundation/goldens/review-mark-reviewed-rolls-back.json index ea34cc544bc..4e7c7c34b81 100644 --- a/mobile/rpc-foundation/goldens/review-mark-reviewed-rolls-back.json +++ b/mobile/rpc-foundation/goldens/review-mark-reviewed-rolls-back.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-open-in-session.json b/mobile/rpc-foundation/goldens/review-open-in-session.json index 24b2de16d2c..b5233b57ca3 100644 --- a/mobile/rpc-foundation/goldens/review-open-in-session.json +++ b/mobile/rpc-foundation/goldens/review-open-in-session.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-send-notes-heals-stale-input.json b/mobile/rpc-foundation/goldens/review-send-notes-heals-stale-input.json index 6913ec396fc..df87eea476c 100644 --- a/mobile/rpc-foundation/goldens/review-send-notes-heals-stale-input.json +++ b/mobile/rpc-foundation/goldens/review-send-notes-heals-stale-input.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-send-sheet-lists-terminals.json b/mobile/rpc-foundation/goldens/review-send-sheet-lists-terminals.json index 49d03e26871..84a46fa40fb 100644 --- a/mobile/rpc-foundation/goldens/review-send-sheet-lists-terminals.json +++ b/mobile/rpc-foundation/goldens/review-send-sheet-lists-terminals.json @@ -3,7 +3,7 @@ "family": "session.review-send-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-stage-file.json b/mobile/rpc-foundation/goldens/review-stage-file.json index 200db25467f..9c7a8ce4d4b 100644 --- a/mobile/rpc-foundation/goldens/review-stage-file.json +++ b/mobile/rpc-foundation/goldens/review-stage-file.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-stage-refused.json b/mobile/rpc-foundation/goldens/review-stage-refused.json index e6e51ecfb3c..d8c21fc875e 100644 --- a/mobile/rpc-foundation/goldens/review-stage-refused.json +++ b/mobile/rpc-foundation/goldens/review-stage-refused.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/sc-base-ref-default.json b/mobile/rpc-foundation/goldens/sc-base-ref-default.json index 0bd5be32c3e..779bb2d81dc 100644 --- a/mobile/rpc-foundation/goldens/sc-base-ref-default.json +++ b/mobile/rpc-foundation/goldens/sc-base-ref-default.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.json b/mobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.json index 3e02f3c1945..bb0b73dff83 100644 --- a/mobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.json +++ b/mobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json b/mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json index b4d848a5899..c212b286e36 100644 --- a/mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json +++ b/mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json b/mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json index 334db434c62..af4bc7ca24f 100644 --- a/mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json +++ b/mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-branch-diff-previewed.json b/mobile/rpc-foundation/goldens/sc-branch-diff-previewed.json index 231cc6c68c9..bbb6ca6f953 100644 --- a/mobile/rpc-foundation/goldens/sc-branch-diff-previewed.json +++ b/mobile/rpc-foundation/goldens/sc-branch-diff-previewed.json @@ -3,7 +3,7 @@ "family": "git.branch-diff-preview", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/sc-changes-loaded.json b/mobile/rpc-foundation/goldens/sc-changes-loaded.json index 45330a9e030..1d708ab4cbf 100644 --- a/mobile/rpc-foundation/goldens/sc-changes-loaded.json +++ b/mobile/rpc-foundation/goldens/sc-changes-loaded.json @@ -3,7 +3,7 @@ "family": "git.changes-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json b/mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json index 1d33974b501..7ace5f71a09 100644 --- a/mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json +++ b/mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json @@ -3,7 +3,7 @@ "family": "git.commit-message-ai", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-commit-message-canceled.json b/mobile/rpc-foundation/goldens/sc-commit-message-canceled.json index dcbd7111a9b..e488944d680 100644 --- a/mobile/rpc-foundation/goldens/sc-commit-message-canceled.json +++ b/mobile/rpc-foundation/goldens/sc-commit-message-canceled.json @@ -3,7 +3,7 @@ "family": "git.commit-message-ai", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-commit-message-generated.json b/mobile/rpc-foundation/goldens/sc-commit-message-generated.json index 5559ed69ffd..3a3fbba996e 100644 --- a/mobile/rpc-foundation/goldens/sc-commit-message-generated.json +++ b/mobile/rpc-foundation/goldens/sc-commit-message-generated.json @@ -3,7 +3,7 @@ "family": "git.commit-message-ai", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-create-existing-review.json b/mobile/rpc-foundation/goldens/sc-create-existing-review.json index ba4bd0b58b3..b5737e433c9 100644 --- a/mobile/rpc-foundation/goldens/sc-create-existing-review.json +++ b/mobile/rpc-foundation/goldens/sc-create-existing-review.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json b/mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json index 190ab60c628..22267848ca5 100644 --- a/mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json +++ b/mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-create-intent-unlisted-provider.json b/mobile/rpc-foundation/goldens/sc-create-intent-unlisted-provider.json index e829d9b8cf3..42a055e177a 100644 --- a/mobile/rpc-foundation/goldens/sc-create-intent-unlisted-provider.json +++ b/mobile/rpc-foundation/goldens/sc-create-intent-unlisted-provider.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.json b/mobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.json index 1f1815b7202..114d3fd3329 100644 --- a/mobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.json +++ b/mobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json b/mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json index 5e261656806..6c151f3eed8 100644 --- a/mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json +++ b/mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-create-refused-empty-message.json b/mobile/rpc-foundation/goldens/sc-create-refused-empty-message.json index 39f5350a457..42f3c1798ae 100644 --- a/mobile/rpc-foundation/goldens/sc-create-refused-empty-message.json +++ b/mobile/rpc-foundation/goldens/sc-create-refused-empty-message.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-create-rejected-empty-message.json b/mobile/rpc-foundation/goldens/sc-create-rejected-empty-message.json index bed68a8f440..280ce445156 100644 --- a/mobile/rpc-foundation/goldens/sc-create-rejected-empty-message.json +++ b/mobile/rpc-foundation/goldens/sc-create-rejected-empty-message.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-eligibility-fetched.json b/mobile/rpc-foundation/goldens/sc-eligibility-fetched.json index 6dfb82c4da8..c9b942c9475 100644 --- a/mobile/rpc-foundation/goldens/sc-eligibility-fetched.json +++ b/mobile/rpc-foundation/goldens/sc-eligibility-fetched.json @@ -3,7 +3,7 @@ "family": "hostedReview.eligibility", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-history-commit-files.json b/mobile/rpc-foundation/goldens/sc-history-commit-files.json index a6c296ae68d..fda5d616e63 100644 --- a/mobile/rpc-foundation/goldens/sc-history-commit-files.json +++ b/mobile/rpc-foundation/goldens/sc-history-commit-files.json @@ -3,7 +3,7 @@ "family": "git.history-commit-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/sc-history-loaded.json b/mobile/rpc-foundation/goldens/sc-history-loaded.json index 688cb63da6f..3b9d93fd137 100644 --- a/mobile/rpc-foundation/goldens/sc-history-loaded.json +++ b/mobile/rpc-foundation/goldens/sc-history-loaded.json @@ -3,7 +3,7 @@ "family": "git.history-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-pr-link-hosted-review.json b/mobile/rpc-foundation/goldens/sc-pr-link-hosted-review.json index 34bd51514ae..a7b7773a1fe 100644 --- a/mobile/rpc-foundation/goldens/sc-pr-link-hosted-review.json +++ b/mobile/rpc-foundation/goldens/sc-pr-link-hosted-review.json @@ -3,7 +3,7 @@ "family": "worktree.review-link", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-pr-link-read.json b/mobile/rpc-foundation/goldens/sc-pr-link-read.json index 4b79af23456..de57c8ad30f 100644 --- a/mobile/rpc-foundation/goldens/sc-pr-link-read.json +++ b/mobile/rpc-foundation/goldens/sc-pr-link-read.json @@ -3,7 +3,7 @@ "family": "worktree.review-link", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-pr-link-set.json b/mobile/rpc-foundation/goldens/sc-pr-link-set.json index 6245291f9b0..a77d23c8563 100644 --- a/mobile/rpc-foundation/goldens/sc-pr-link-set.json +++ b/mobile/rpc-foundation/goldens/sc-pr-link-set.json @@ -3,7 +3,7 @@ "family": "worktree.review-link", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json b/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json index 5aed9372ed1..dd582353867 100644 --- a/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json +++ b/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json @@ -3,7 +3,7 @@ "family": "hostedReview.eligibility", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.json b/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.json index c6ce63dba12..de46aedeb4f 100644 --- a/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.json +++ b/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.json @@ -3,7 +3,7 @@ "family": "hostedReview.eligibility", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.json b/mobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.json index 5ad745af476..bacda6ec4e0 100644 --- a/mobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.json +++ b/mobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.json @@ -3,7 +3,7 @@ "family": "git.remote-prerequisite", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-prerequisite-publish.json b/mobile/rpc-foundation/goldens/sc-prerequisite-publish.json index 0aa7ba9049a..c67e3512986 100644 --- a/mobile/rpc-foundation/goldens/sc-prerequisite-publish.json +++ b/mobile/rpc-foundation/goldens/sc-prerequisite-publish.json @@ -3,7 +3,7 @@ "family": "git.remote-prerequisite", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-prerequisite-push.json b/mobile/rpc-foundation/goldens/sc-prerequisite-push.json index 1fb00dbbe08..264198b17ad 100644 --- a/mobile/rpc-foundation/goldens/sc-prerequisite-push.json +++ b/mobile/rpc-foundation/goldens/sc-prerequisite-push.json @@ -3,7 +3,7 @@ "family": "git.remote-prerequisite", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json b/mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json index 8ce664bb345..1ec00f72d84 100644 --- a/mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json +++ b/mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json @@ -3,7 +3,7 @@ "family": "git.remote-prerequisite", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-reveal-first-poll.json b/mobile/rpc-foundation/goldens/sc-reveal-first-poll.json index 5f24e1f7000..b4df257bda7 100644 --- a/mobile/rpc-foundation/goldens/sc-reveal-first-poll.json +++ b/mobile/rpc-foundation/goldens/sc-reveal-first-poll.json @@ -3,7 +3,7 @@ "family": "session.tab-reveal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-reveal-timeout.json b/mobile/rpc-foundation/goldens/sc-reveal-timeout.json index 90a2b1bb083..5c91f1cbb99 100644 --- a/mobile/rpc-foundation/goldens/sc-reveal-timeout.json +++ b/mobile/rpc-foundation/goldens/sc-reveal-timeout.json @@ -3,7 +3,7 @@ "family": "session.tab-reveal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json b/mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json index 7bd28f65347..20d345ee3ad 100644 --- a/mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json +++ b/mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.json b/mobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.json index 5582621ee67..e3301abd1ad 100644 --- a/mobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.json +++ b/mobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-review-commit-rejected.json b/mobile/rpc-foundation/goldens/sc-review-commit-rejected.json index db321e4e48a..e63cf0b23b9 100644 --- a/mobile/rpc-foundation/goldens/sc-review-commit-rejected.json +++ b/mobile/rpc-foundation/goldens/sc-review-commit-rejected.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-review-commit.json b/mobile/rpc-foundation/goldens/sc-review-commit.json index d74c460731b..524741d37d9 100644 --- a/mobile/rpc-foundation/goldens/sc-review-commit.json +++ b/mobile/rpc-foundation/goldens/sc-review-commit.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-review-status-entries-not-array.json b/mobile/rpc-foundation/goldens/sc-review-status-entries-not-array.json index 3f11935a97c..deff2022d7e 100644 --- a/mobile/rpc-foundation/goldens/sc-review-status-entries-not-array.json +++ b/mobile/rpc-foundation/goldens/sc-review-status-entries-not-array.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-review-status-normalized.json b/mobile/rpc-foundation/goldens/sc-review-status-normalized.json index 51a267ca07b..eacaa81418f 100644 --- a/mobile/rpc-foundation/goldens/sc-review-status-normalized.json +++ b/mobile/rpc-foundation/goldens/sc-review-status-normalized.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/schedules-b3.json b/mobile/rpc-foundation/goldens/schedules-b3.json index b797ea3713d..7c9c2183c53 100644 --- a/mobile/rpc-foundation/goldens/schedules-b3.json +++ b/mobile/rpc-foundation/goldens/schedules-b3.json @@ -3,7 +3,7 @@ "family": "linear-detail-barrier", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.json b/mobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.json index e625f64c47a..0dac83a7030 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.json b/mobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.json index e5006d9a13d..3cf50f6238e 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json b/mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json index d7fe2b57c81..779d541b009 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.json b/mobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.json index 707b3b3912c..9c18239d5bc 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.json b/mobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.json index 042aa981983..f7e4a6f755f 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.json b/mobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.json index 84978f6fe1f..63df1d386a8 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/session-browser-tab-created.json b/mobile/rpc-foundation/goldens/session-browser-tab-created.json index 62636b04ef0..2d5e08b1b71 100644 --- a/mobile/rpc-foundation/goldens/session-browser-tab-created.json +++ b/mobile/rpc-foundation/goldens/session-browser-tab-created.json @@ -3,7 +3,7 @@ "family": "session.browser-tab-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-create-browser-refused.json b/mobile/rpc-foundation/goldens/session-create-browser-refused.json index 55a72e1853a..9983ad3a35b 100644 --- a/mobile/rpc-foundation/goldens/session-create-browser-refused.json +++ b/mobile/rpc-foundation/goldens/session-create-browser-refused.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-create-browser-tab.json b/mobile/rpc-foundation/goldens/session-create-browser-tab.json index c6fbdb84f80..983b8a25be6 100644 --- a/mobile/rpc-foundation/goldens/session-create-browser-tab.json +++ b/mobile/rpc-foundation/goldens/session-create-browser-tab.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-create-markdown-name-collision.json b/mobile/rpc-foundation/goldens/session-create-markdown-name-collision.json index 69ff1e45ce0..c9924c284ac 100644 --- a/mobile/rpc-foundation/goldens/session-create-markdown-name-collision.json +++ b/mobile/rpc-foundation/goldens/session-create-markdown-name-collision.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-create-markdown-note.json b/mobile/rpc-foundation/goldens/session-create-markdown-note.json index f9ee9130843..8389c1a288e 100644 --- a/mobile/rpc-foundation/goldens/session-create-markdown-note.json +++ b/mobile/rpc-foundation/goldens/session-create-markdown-note.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-ignores-a-second-create-in-flight.json b/mobile/rpc-foundation/goldens/session-create-terminal-ignores-a-second-create-in-flight.json index 584f77e46bf..239b59db7b7 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-ignores-a-second-create-in-flight.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-ignores-a-second-create-in-flight.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-launches-an-agent-quick-command.json b/mobile/rpc-foundation/goldens/session-create-terminal-launches-an-agent-quick-command.json index c92a2099aa0..e2073a59e46 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-launches-an-agent-quick-command.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-launches-an-agent-quick-command.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-refused.json b/mobile/rpc-foundation/goldens/session-create-terminal-refused.json index cb87f9b0626..ffefbbd3ad7 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-refused.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-refused.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-replaces-active.json b/mobile/rpc-foundation/goldens/session-create-terminal-replaces-active.json index 85cd835be53..8761aa773f2 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-replaces-active.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-replaces-active.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-runs-a-quick-command.json b/mobile/rpc-foundation/goldens/session-create-terminal-runs-a-quick-command.json index 1abfaa447bb..447d8b1fd84 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-runs-a-quick-command.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-runs-a-quick-command.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-with-prompt.json b/mobile/rpc-foundation/goldens/session-create-terminal-with-prompt.json index 56522d73be0..9227a9bbd26 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-with-prompt.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-with-prompt.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-without-active-tab.json b/mobile/rpc-foundation/goldens/session-create-terminal-without-active-tab.json index cab89fc5bc0..9ac1a36b9f4 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-without-active-tab.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-without-active-tab.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-without-handle.json b/mobile/rpc-foundation/goldens/session-create-terminal-without-handle.json index 25a3fb7a07a..e795bb78dad 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-without-handle.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-without-handle.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-diff-notes-load-refused.json b/mobile/rpc-foundation/goldens/session-diff-notes-load-refused.json index b6c37d3dfe6..f7af0614b99 100644 --- a/mobile/rpc-foundation/goldens/session-diff-notes-load-refused.json +++ b/mobile/rpc-foundation/goldens/session-diff-notes-load-refused.json @@ -3,7 +3,7 @@ "family": "session.diff-notes", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/session-diff-notes-loaded.json b/mobile/rpc-foundation/goldens/session-diff-notes-loaded.json index e97f6f3a88e..882b39a8788 100644 --- a/mobile/rpc-foundation/goldens/session-diff-notes-loaded.json +++ b/mobile/rpc-foundation/goldens/session-diff-notes-loaded.json @@ -3,7 +3,7 @@ "family": "session.diff-notes", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/session-file-tab-read.json b/mobile/rpc-foundation/goldens/session-file-tab-read.json index 86d3316b451..a81e48035cb 100644 --- a/mobile/rpc-foundation/goldens/session-file-tab-read.json +++ b/mobile/rpc-foundation/goldens/session-file-tab-read.json @@ -3,7 +3,7 @@ "family": "session.tab-documents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-markdown-disk-read.json b/mobile/rpc-foundation/goldens/session-markdown-disk-read.json new file mode 100644 index 00000000000..f1aadae09a2 --- /dev/null +++ b/mobile/rpc-foundation/goldens/session-markdown-disk-read.json @@ -0,0 +1,140 @@ +{ + "operation": "session.tab-documents", + "family": "session.markdown-disk-fallback", + "namedDeltas": [], + "runnerVersion": 1, + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", + "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", + "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", + "scenarioSha256": "29565833b19968faa701f656d166050419f149d6bb76d1f63f4dd3af76b8e896", + "platform": "darwin", + "scenarioVersion": 1, + "projectionVersion": 2, + "goldenFormatVersion": 5, + "values": { + "44abbe2af7aa": { + "name": "markdown.readTab#1", + "ordinal": 2, + "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"markdown.readTab\",\"params\":{\"worktree\":\"id:workspace-1\",\"tabId\":\"tab-md\"}}" + }, + "c6cea5310098": { + "file": {}, + "markdown": { + "tab-md": { + "baseVersion": "", + "content": "# disk", + "editable": false, + "isDirty": false, + "localContent": "# disk", + "readOnlyReason": "Editing needs Orca desktop running.", + "stale": false, + "status": "ready" + } + } + }, + "d4be01606497": { + "name": "files.read#1", + "ordinal": 4, + "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"files.read\",\"params\":{\"worktree\":\"id:workspace-1\",\"relativePath\":\"docs/readme.md\"}}" + }, + "d59404d25d6c": { + "name": "files.read#1", + "ordinal": 3, + "args": [ + { + "name": "method", + "value": "files.read" + }, + { + "name": "params", + "value": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "byteLength": 6, + "content": "# disk", + "truncated": false + } + } + } + }, + "e258dafc91f1": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "error": { + "code": "renderer_unavailable", + "message": "Renderer unavailable" + }, + "id": "frame-1", + "ok": false + } + } + }, + "eb79a9b3682a": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "$rpc": "undefined" + } + } + }, + "recording": { + "scenario": "session-markdown-disk-read", + "checkpoints": [ + { + "id": "fell-back", + "observation": { + "sender": ["e258dafc91f1", "d59404d25d6c"], + "payloads": ["44abbe2af7aa", "d4be01606497"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "c6cea5310098", + "effects": [] + } + } + ] + } +} diff --git a/mobile/rpc-foundation/goldens/session-markdown-disk-served.json b/mobile/rpc-foundation/goldens/session-markdown-disk-served.json new file mode 100644 index 00000000000..0d93f4221d5 --- /dev/null +++ b/mobile/rpc-foundation/goldens/session-markdown-disk-served.json @@ -0,0 +1,102 @@ +{ + "operation": "session.tab-documents", + "family": "session.markdown-disk-fallback", + "namedDeltas": [], + "runnerVersion": 1, + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", + "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", + "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", + "scenarioSha256": "8375df17afabf282a1d37ffaa3a334b1fa9105ba04e6618f16082f66bfe3f76d", + "platform": "darwin", + "scenarioVersion": 1, + "projectionVersion": 2, + "goldenFormatVersion": 5, + "values": { + "0d6b0adc036f": { + "name": "markdown.readTab#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "markdown.readTab" + }, + { + "name": "params", + "value": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "content": "# served", + "editable": true, + "isDirty": false, + "version": "v1" + } + } + } + }, + "3e3efaab3a20": { + "file": {}, + "markdown": { + "tab-md": { + "baseVersion": "v1", + "content": "# served", + "editable": true, + "isDirty": false, + "localContent": "# served", + "readOnlyReason": { + "$rpc": "undefined" + }, + "stale": false, + "status": "ready" + } + } + }, + "44abbe2af7aa": { + "name": "markdown.readTab#1", + "ordinal": 2, + "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"markdown.readTab\",\"params\":{\"worktree\":\"id:workspace-1\",\"tabId\":\"tab-md\"}}" + }, + "eb79a9b3682a": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "$rpc": "undefined" + } + } + }, + "recording": { + "scenario": "session-markdown-disk-served", + "checkpoints": [ + { + "id": "served", + "observation": { + "sender": ["0d6b0adc036f"], + "payloads": ["44abbe2af7aa"], + "settlements": { + "markdown": "eb79a9b3682a" + }, + "state": "3e3efaab3a20", + "effects": [] + } + } + ] + } +} diff --git a/mobile/rpc-foundation/goldens/session-markdown-save-conflict.json b/mobile/rpc-foundation/goldens/session-markdown-save-conflict.json index 8f4990d42eb..ab26ea3f89f 100644 --- a/mobile/rpc-foundation/goldens/session-markdown-save-conflict.json +++ b/mobile/rpc-foundation/goldens/session-markdown-save-conflict.json @@ -3,7 +3,7 @@ "family": "session.markdown-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/session-markdown-saved.json b/mobile/rpc-foundation/goldens/session-markdown-saved.json index b1435b3b624..85a6c21322b 100644 --- a/mobile/rpc-foundation/goldens/session-markdown-saved.json +++ b/mobile/rpc-foundation/goldens/session-markdown-saved.json @@ -3,7 +3,7 @@ "family": "session.markdown-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/session-markdown-tab-disk-fallback.json b/mobile/rpc-foundation/goldens/session-markdown-tab-disk-fallback.json index 506f1011d85..71d1fb90f67 100644 --- a/mobile/rpc-foundation/goldens/session-markdown-tab-disk-fallback.json +++ b/mobile/rpc-foundation/goldens/session-markdown-tab-disk-fallback.json @@ -3,7 +3,7 @@ "family": "session.tab-documents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-markdown-tab-read.json b/mobile/rpc-foundation/goldens/session-markdown-tab-read.json index 7dcb8ebfcc9..658ef42a546 100644 --- a/mobile/rpc-foundation/goldens/session-markdown-tab-read.json +++ b/mobile/rpc-foundation/goldens/session-markdown-tab-read.json @@ -3,7 +3,7 @@ "family": "session.tab-documents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-markdown-tab-refused.json b/mobile/rpc-foundation/goldens/session-markdown-tab-refused.json index 22cfe8a4cef..be2359bf3e1 100644 --- a/mobile/rpc-foundation/goldens/session-markdown-tab-refused.json +++ b/mobile/rpc-foundation/goldens/session-markdown-tab-refused.json @@ -3,7 +3,7 @@ "family": "session.tab-documents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-startup-both-activation-sites.json b/mobile/rpc-foundation/goldens/session-startup-both-activation-sites.json index 391d9e45091..eba885eb2ab 100644 --- a/mobile/rpc-foundation/goldens/session-startup-both-activation-sites.json +++ b/mobile/rpc-foundation/goldens/session-startup-both-activation-sites.json @@ -3,7 +3,7 @@ "family": "session.startup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "6b08c394e37fd572cf63a4c11934247d379008f11117aa5af33b2926fbd32d1e", diff --git a/mobile/rpc-foundation/goldens/session-startup-floating-route-skips-activation.json b/mobile/rpc-foundation/goldens/session-startup-floating-route-skips-activation.json index f32c7d9868a..862b1cfadbf 100644 --- a/mobile/rpc-foundation/goldens/session-startup-floating-route-skips-activation.json +++ b/mobile/rpc-foundation/goldens/session-startup-floating-route-skips-activation.json @@ -3,7 +3,7 @@ "family": "session.startup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "6b08c394e37fd572cf63a4c11934247d379008f11117aa5af33b2926fbd32d1e", diff --git a/mobile/rpc-foundation/goldens/session-startup-keeps-terminals-visible-on-reconnect.json b/mobile/rpc-foundation/goldens/session-startup-keeps-terminals-visible-on-reconnect.json index 2066dffd10e..c3ada63e52c 100644 --- a/mobile/rpc-foundation/goldens/session-startup-keeps-terminals-visible-on-reconnect.json +++ b/mobile/rpc-foundation/goldens/session-startup-keeps-terminals-visible-on-reconnect.json @@ -3,7 +3,7 @@ "family": "session.startup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "6b08c394e37fd572cf63a4c11934247d379008f11117aa5af33b2926fbd32d1e", diff --git a/mobile/rpc-foundation/goldens/session-startup-refused-tab-load-still-loads-terminals.json b/mobile/rpc-foundation/goldens/session-startup-refused-tab-load-still-loads-terminals.json index 855a752a8eb..5b00f80d5ff 100644 --- a/mobile/rpc-foundation/goldens/session-startup-refused-tab-load-still-loads-terminals.json +++ b/mobile/rpc-foundation/goldens/session-startup-refused-tab-load-still-loads-terminals.json @@ -3,7 +3,7 @@ "family": "session.startup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "6b08c394e37fd572cf63a4c11934247d379008f11117aa5af33b2926fbd32d1e", diff --git a/mobile/rpc-foundation/goldens/session-tab-activation-focus-and-activate.json b/mobile/rpc-foundation/goldens/session-tab-activation-focus-and-activate.json index 851ab342a43..15a12ffc31a 100644 --- a/mobile/rpc-foundation/goldens/session-tab-activation-focus-and-activate.json +++ b/mobile/rpc-foundation/goldens/session-tab-activation-focus-and-activate.json @@ -3,7 +3,7 @@ "family": "session.tab-activation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-tab-activation-refused.json b/mobile/rpc-foundation/goldens/session-tab-activation-refused.json index 65428d7bcac..8e01f39e9b4 100644 --- a/mobile/rpc-foundation/goldens/session-tab-activation-refused.json +++ b/mobile/rpc-foundation/goldens/session-tab-activation-refused.json @@ -3,7 +3,7 @@ "family": "session.tab-activation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-tab-activation-transport-error.json b/mobile/rpc-foundation/goldens/session-tab-activation-transport-error.json index f38d615ca93..87cf6fd02c8 100644 --- a/mobile/rpc-foundation/goldens/session-tab-activation-transport-error.json +++ b/mobile/rpc-foundation/goldens/session-tab-activation-transport-error.json @@ -3,7 +3,7 @@ "family": "session.tab-activation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-tab-close-refused-keeps-tab.json b/mobile/rpc-foundation/goldens/session-tab-close-refused-keeps-tab.json index 889782a0ed6..1a5cd7d5856 100644 --- a/mobile/rpc-foundation/goldens/session-tab-close-refused-keeps-tab.json +++ b/mobile/rpc-foundation/goldens/session-tab-close-refused-keeps-tab.json @@ -3,7 +3,7 @@ "family": "session.tab-close", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-tab-close-session-tab.json b/mobile/rpc-foundation/goldens/session-tab-close-session-tab.json index 0d7736cb405..c09669d8612 100644 --- a/mobile/rpc-foundation/goldens/session-tab-close-session-tab.json +++ b/mobile/rpc-foundation/goldens/session-tab-close-session-tab.json @@ -3,7 +3,7 @@ "family": "session.tab-close", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-tab-close-terminal.json b/mobile/rpc-foundation/goldens/session-tab-close-terminal.json index a96e597d224..12ef82b2296 100644 --- a/mobile/rpc-foundation/goldens/session-tab-close-terminal.json +++ b/mobile/rpc-foundation/goldens/session-tab-close-terminal.json @@ -3,7 +3,7 @@ "family": "session.tab-close", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-tab-closed.json b/mobile/rpc-foundation/goldens/session-tab-closed.json index 38d9c8713da..5783b081515 100644 --- a/mobile/rpc-foundation/goldens/session-tab-closed.json +++ b/mobile/rpc-foundation/goldens/session-tab-closed.json @@ -3,7 +3,7 @@ "family": "session.tab-close-session", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-tab-rename.json b/mobile/rpc-foundation/goldens/session-tab-rename.json index 091f028d644..2feec36471c 100644 --- a/mobile/rpc-foundation/goldens/session-tab-rename.json +++ b/mobile/rpc-foundation/goldens/session-tab-rename.json @@ -3,7 +3,7 @@ "family": "session.tab-close", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-tab-renamed.json b/mobile/rpc-foundation/goldens/session-tab-renamed.json index 395cf9e72ed..6993ca54fc3 100644 --- a/mobile/rpc-foundation/goldens/session-tab-renamed.json +++ b/mobile/rpc-foundation/goldens/session-tab-renamed.json @@ -3,7 +3,7 @@ "family": "session.tab-rename", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-tabs-health-errored.json b/mobile/rpc-foundation/goldens/session-tabs-health-errored.json index 977089af9f4..8b90c593fc5 100644 --- a/mobile/rpc-foundation/goldens/session-tabs-health-errored.json +++ b/mobile/rpc-foundation/goldens/session-tabs-health-errored.json @@ -3,7 +3,7 @@ "family": "session.tabs-stream-health", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-tabs-health-reconciled.json b/mobile/rpc-foundation/goldens/session-tabs-health-reconciled.json index f866c52de56..75f0c25bde1 100644 --- a/mobile/rpc-foundation/goldens/session-tabs-health-reconciled.json +++ b/mobile/rpc-foundation/goldens/session-tabs-health-reconciled.json @@ -3,7 +3,7 @@ "family": "session.tabs-stream-health", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-tabs-health-refused.json b/mobile/rpc-foundation/goldens/session-tabs-health-refused.json index 55f081b55e1..aa329965e23 100644 --- a/mobile/rpc-foundation/goldens/session-tabs-health-refused.json +++ b/mobile/rpc-foundation/goldens/session-tabs-health-refused.json @@ -3,7 +3,7 @@ "family": "session.tabs-stream-health", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-tabs-health-stale-application-revision.json b/mobile/rpc-foundation/goldens/session-tabs-health-stale-application-revision.json index fdd90ebe14a..16884a243a6 100644 --- a/mobile/rpc-foundation/goldens/session-tabs-health-stale-application-revision.json +++ b/mobile/rpc-foundation/goldens/session-tabs-health-stale-application-revision.json @@ -3,7 +3,7 @@ "family": "session.tabs-stream-health", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-take-floor.json b/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-take-floor.json index 8656a67a836..a9a68e933be 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-take-floor.json +++ b/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-take-floor.json @@ -3,7 +3,7 @@ "family": "session.terminal-display-mode", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9e90ad39a8d4257adf30166757a6364c4710dc3ae9f06365f80a3dd8f1c94d89", diff --git a/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-device-token.json b/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-device-token.json index 1d0dc12e51d..6d56f11ac61 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-device-token.json +++ b/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-device-token.json @@ -3,7 +3,7 @@ "family": "session.terminal-display-mode", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9e90ad39a8d4257adf30166757a6364c4710dc3ae9f06365f80a3dd8f1c94d89", diff --git a/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-viewport.json b/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-viewport.json index 46ef4b161f4..1b64db9e382 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-viewport.json +++ b/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-viewport.json @@ -3,7 +3,7 @@ "family": "session.terminal-display-mode", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9e90ad39a8d4257adf30166757a6364c4710dc3ae9f06365f80a3dd8f1c94d89", diff --git a/mobile/rpc-foundation/goldens/session-terminal-display-mode-drops-second-toggle.json b/mobile/rpc-foundation/goldens/session-terminal-display-mode-drops-second-toggle.json index e02e028f749..1c5903a5d6d 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-display-mode-drops-second-toggle.json +++ b/mobile/rpc-foundation/goldens/session-terminal-display-mode-drops-second-toggle.json @@ -3,7 +3,7 @@ "family": "session.terminal-display-mode", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9e90ad39a8d4257adf30166757a6364c4710dc3ae9f06365f80a3dd8f1c94d89", diff --git a/mobile/rpc-foundation/goldens/session-terminal-display-mode-to-desktop.json b/mobile/rpc-foundation/goldens/session-terminal-display-mode-to-desktop.json index ee773e53988..92ff0d69ad8 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-display-mode-to-desktop.json +++ b/mobile/rpc-foundation/goldens/session-terminal-display-mode-to-desktop.json @@ -3,7 +3,7 @@ "family": "session.terminal-display-mode", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9e90ad39a8d4257adf30166757a6364c4710dc3ae9f06365f80a3dd8f1c94d89", diff --git a/mobile/rpc-foundation/goldens/session-terminal-list-dedupes-handles.json b/mobile/rpc-foundation/goldens/session-terminal-list-dedupes-handles.json index 29798870843..ce2c981fcc6 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-list-dedupes-handles.json +++ b/mobile/rpc-foundation/goldens/session-terminal-list-dedupes-handles.json @@ -3,7 +3,7 @@ "family": "session.terminal-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-terminal-list-empty-guarded.json b/mobile/rpc-foundation/goldens/session-terminal-list-empty-guarded.json index eb960094204..bc67e4d906b 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-list-empty-guarded.json +++ b/mobile/rpc-foundation/goldens/session-terminal-list-empty-guarded.json @@ -3,7 +3,7 @@ "family": "session.terminal-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-terminal-list-merged.json b/mobile/rpc-foundation/goldens/session-terminal-list-merged.json index 2757872ee30..ae2400ee3fb 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-list-merged.json +++ b/mobile/rpc-foundation/goldens/session-terminal-list-merged.json @@ -3,7 +3,7 @@ "family": "session.terminal-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-terminal-list-refused.json b/mobile/rpc-foundation/goldens/session-terminal-list-refused.json index ee75e846dc7..facb44a2774 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-list-refused.json +++ b/mobile/rpc-foundation/goldens/session-terminal-list-refused.json @@ -3,7 +3,7 @@ "family": "session.terminal-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.json b/mobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.json index 4a81d9d6bc0..076ef068215 100644 --- a/mobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json b/mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json index 20cd472fcd7..c3165ecc277 100644 --- a/mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json +++ b/mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-bot-overrides-refused.json b/mobile/rpc-foundation/goldens/settings-bot-overrides-refused.json index 3379ad2ddbf..a1c85cbd138 100644 --- a/mobile/rpc-foundation/goldens/settings-bot-overrides-refused.json +++ b/mobile/rpc-foundation/goldens/settings-bot-overrides-refused.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.json b/mobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.json index 1d6e3705b92..3488d08cd40 100644 --- a/mobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-home-coalesced.json b/mobile/rpc-foundation/goldens/settings-home-coalesced.json index cff3a0f248f..dc69d3ffa45 100644 --- a/mobile/rpc-foundation/goldens/settings-home-coalesced.json +++ b/mobile/rpc-foundation/goldens/settings-home-coalesced.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-home-providers-fulfilled.json b/mobile/rpc-foundation/goldens/settings-home-providers-fulfilled.json index 253e3a90cb4..1415f0dfe4b 100644 --- a/mobile/rpc-foundation/goldens/settings-home-providers-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-home-providers-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json b/mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json index 12398d49e62..d2c9191234f 100644 --- a/mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json +++ b/mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-home-providers-refused.json b/mobile/rpc-foundation/goldens/settings-home-providers-refused.json index 00ced816288..3c87c93dd28 100644 --- a/mobile/rpc-foundation/goldens/settings-home-providers-refused.json +++ b/mobile/rpc-foundation/goldens/settings-home-providers-refused.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-home-providers-transport-error.json b/mobile/rpc-foundation/goldens/settings-home-providers-transport-error.json index ecfdfe5479d..bde81da5c50 100644 --- a/mobile/rpc-foundation/goldens/settings-home-providers-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-home-providers-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-new-tab-refused.json b/mobile/rpc-foundation/goldens/settings-new-tab-refused.json index 6cdcd10c8a4..6d4fa59273c 100644 --- a/mobile/rpc-foundation/goldens/settings-new-tab-refused.json +++ b/mobile/rpc-foundation/goldens/settings-new-tab-refused.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/settings-new-tab-ssh.json b/mobile/rpc-foundation/goldens/settings-new-tab-ssh.json index 735352598d4..efa898d4e1c 100644 --- a/mobile/rpc-foundation/goldens/settings-new-tab-ssh.json +++ b/mobile/rpc-foundation/goldens/settings-new-tab-ssh.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json b/mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json index 81ebb8d2245..3aa65cfa1bf 100644 --- a/mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json b/mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json index 26c5fa79814..d37e94181b2 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json +++ b/mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json index 396c5db71b8..111b6a14dfe 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json new file mode 100644 index 00000000000..6b6ae3ee351 --- /dev/null +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json @@ -0,0 +1,469 @@ +{ + "operation": "settings.repo-metadata", + "family": "settings.repo-metadata", + "namedDeltas": [], + "runnerVersion": 1, + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", + "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", + "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", + "scenarioSha256": "e67b645d8c4fa42e93d4cf36f45311922f7b9d9d8cc597a12f55c664d840901b", + "platform": "darwin", + "scenarioVersion": 1, + "projectionVersion": 2, + "goldenFormatVersion": 5, + "values": { + "12ba3f41d4db": { + "name": "hostPlatform", + "ordinal": 14, + "value": "darwin" + }, + "19b2979d7fc2": { + "name": "repo.list#1", + "ordinal": 2, + "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}" + }, + "19bf6d2469a6": { + "name": "repoColorsByName", + "ordinal": 3, + "value": [ + ["Lucide", "#123456"], + ["Emoji", "#f97316"], + ["Avatar", "#f97316"] + ] + }, + "1a160fc2d137": { + "name": "host.platform#1", + "ordinal": 9, + "args": [ + { + "name": "method", + "value": "host.platform" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "pending", + "startedAt": 0 + } + }, + "36a96ddbd396": { + "name": "host.platform#1", + "ordinal": 12, + "json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}" + }, + "56ab81cb67dc": { + "name": "hostLabelById", + "ordinal": 13, + "value": [["ssh:ssh-1", "SSH"]] + }, + "6338d57f282f": { + "repoColorsByName": [ + ["Lucide", "#123456"], + ["Emoji", "#f97316"], + ["Avatar", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"], + ["repo-3", "ssh:ssh-1"] + ], + "repoIconsByName": [ + [ + "Lucide", + { + "name": "Rocket", + "type": "lucide" + } + ], + [ + "Emoji", + { + "emoji": "🐳", + "type": "emoji" + } + ], + [ + "Avatar", + { + "label": "acme/orca", + "source": "github", + "src": "https://github.com/acme.png?size=64", + "type": "image" + } + ] + ], + "repoIdsByName": [ + ["Lucide", "repo-1"], + ["Emoji", "repo-2"], + ["Avatar", "repo-3"] + ] + }, + "64f5522cc303": { + "name": "settings.get#1", + "ordinal": 8, + "args": [ + { + "name": "method", + "value": "settings.get" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-3", + "ok": true, + "result": { + "settings": { + "defaultTuiAgent": "codex", + "disabledTuiAgents": [], + "hostSettingOverrides": {} + } + } + } + } + }, + "668a7e313975": { + "name": "ssh.listTargetSummaries#1", + "ordinal": 10, + "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.listTargetSummaries\"}" + }, + "6bc5e5b7a259": { + "name": "repoHostIdByRepoId", + "ordinal": 6, + "value": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"], + ["repo-3", "ssh:ssh-1"] + ] + }, + "77cbfd1f087c": { + "name": "repoIdsByName", + "ordinal": 5, + "value": [ + ["Lucide", "repo-1"], + ["Emoji", "repo-2"], + ["Avatar", "repo-3"] + ] + }, + "9270aeb7d9c6": { + "status": "pending", + "startedAt": 0 + }, + "96e0c54d86b0": { + "name": "settings.get#1", + "ordinal": 8, + "args": [ + { + "name": "method", + "value": "settings.get" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "pending", + "startedAt": 0 + } + }, + "a9add325bb0a": { + "name": "repo.list#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "repo.list" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "repos": [ + { + "badgeColor": "#123456", + "connectionId": { + "$rpc": "null" + }, + "displayName": "Lucide", + "id": "repo-1", + "repoIcon": { + "name": "Rocket", + "type": "lucide" + } + }, + { + "connectionId": "ssh-1", + "displayName": "Emoji", + "id": "repo-2", + "repoIcon": { + "emoji": "🐳", + "type": "emoji" + } + }, + { + "displayName": "Avatar", + "executionHostId": "ssh:ssh-1", + "id": "repo-3", + "repoIcon": { + "label": "acme/orca", + "source": "github", + "src": "https://github.com/acme.png?size=64", + "type": "image" + } + } + ] + } + } + } + }, + "ae27a5b525fa": { + "name": "repoIconsByName", + "ordinal": 4, + "value": [ + [ + "Lucide", + { + "name": "Rocket", + "type": "lucide" + } + ], + [ + "Emoji", + { + "emoji": "🐳", + "type": "emoji" + } + ], + [ + "Avatar", + { + "label": "acme/orca", + "source": "github", + "src": "https://github.com/acme.png?size=64", + "type": "image" + } + ] + ] + }, + "b428bce85ee7": { + "name": "settings.get#1", + "ordinal": 11, + "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" + }, + "c752e67787f9": { + "name": "ssh.listTargetSummaries#1", + "ordinal": 7, + "args": [ + { + "name": "method", + "value": "ssh.listTargetSummaries" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "targets": [ + { + "id": "ssh-1", + "label": "SSH" + } + ] + } + } + } + }, + "e62b46a04ba1": { + "hostLabelById": [["ssh:ssh-1", "SSH"]], + "hostPlatform": "darwin", + "repoColorsByName": [ + ["Lucide", "#123456"], + ["Emoji", "#f97316"], + ["Avatar", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"], + ["repo-3", "ssh:ssh-1"] + ], + "repoIconsByName": [ + [ + "Lucide", + { + "name": "Rocket", + "type": "lucide" + } + ], + [ + "Emoji", + { + "emoji": "🐳", + "type": "emoji" + } + ], + [ + "Avatar", + { + "label": "acme/orca", + "source": "github", + "src": "https://github.com/acme.png?size=64", + "type": "image" + } + ] + ], + "repoIdsByName": [ + ["Lucide", "repo-1"], + ["Emoji", "repo-2"], + ["Avatar", "repo-3"] + ] + }, + "eb79a9b3682a": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "$rpc": "undefined" + } + }, + "ee8e16df5187": { + "name": "host.platform#1", + "ordinal": 9, + "args": [ + { + "name": "method", + "value": "host.platform" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-4", + "ok": true, + "result": { + "platform": "darwin" + } + } + } + } + }, + "recording": { + "scenario": "settings-repo-metadata-icons", + "checkpoints": [ + { + "id": "settings-pending", + "observation": { + "sender": ["a9add325bb0a", "c752e67787f9", "96e0c54d86b0", "1a160fc2d137"], + "payloads": ["19b2979d7fc2", "668a7e313975", "b428bce85ee7", "36a96ddbd396"], + "settlements": { + "mount": "eb79a9b3682a", + "load": "9270aeb7d9c6" + }, + "state": "6338d57f282f", + "effects": ["19bf6d2469a6", "ae27a5b525fa", "77cbfd1f087c", "6bc5e5b7a259"] + } + }, + { + "id": "settled", + "observation": { + "sender": ["a9add325bb0a", "c752e67787f9", "64f5522cc303", "ee8e16df5187"], + "payloads": ["19b2979d7fc2", "668a7e313975", "b428bce85ee7", "36a96ddbd396"], + "settlements": { + "mount": "eb79a9b3682a", + "load": "eb79a9b3682a" + }, + "state": "e62b46a04ba1", + "effects": [ + "19bf6d2469a6", + "ae27a5b525fa", + "77cbfd1f087c", + "6bc5e5b7a259", + "56ab81cb67dc", + "12ba3f41d4db" + ] + } + } + ] + } +} diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json index 63609ba7f5d..0fc7ff78880 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json index 365e9fe7457..54a3384af19 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json index 6385c79af0f..17253bc69bb 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json index 1103c1a2888..84ce6519066 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json b/mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json index db67b17d9f8..eb7225424c7 100644 --- a/mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json b/mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json index 38255a65b74..e2eae5d35ff 100644 --- a/mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json +++ b/mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-resume-metadata-refused.json b/mobile/rpc-foundation/goldens/settings-resume-metadata-refused.json index 778d0b59b80..3a6b0588859 100644 --- a/mobile/rpc-foundation/goldens/settings-resume-metadata-refused.json +++ b/mobile/rpc-foundation/goldens/settings-resume-metadata-refused.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.json b/mobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.json index f1459fa4f81..d6f480e990d 100644 --- a/mobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.json b/mobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.json index 1b8de276f0d..2e465e0567b 100644 --- a/mobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json b/mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json index d4d57ada256..12f1e31f9ba 100644 --- a/mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json +++ b/mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-task-hydration-refused.json b/mobile/rpc-foundation/goldens/settings-task-hydration-refused.json index 045bf076e6b..d4898ec62ed 100644 --- a/mobile/rpc-foundation/goldens/settings-task-hydration-refused.json +++ b/mobile/rpc-foundation/goldens/settings-task-hydration-refused.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json b/mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json index cfef7446c4e..e5cf7018607 100644 --- a/mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-task-workspace-create-linear.json b/mobile/rpc-foundation/goldens/settings-task-workspace-create-linear.json index e02c9a604fd..de903acfe74 100644 --- a/mobile/rpc-foundation/goldens/settings-task-workspace-create-linear.json +++ b/mobile/rpc-foundation/goldens/settings-task-workspace-create-linear.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json b/mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json index 3c54db00768..0bb54e80434 100644 --- a/mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json +++ b/mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.json b/mobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.json index 7b39d1a33cf..f21675f15e4 100644 --- a/mobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-task-workspace-refused.json b/mobile/rpc-foundation/goldens/settings-task-workspace-refused.json index 3685477977b..e6f94ef89fe 100644 --- a/mobile/rpc-foundation/goldens/settings-task-workspace-refused.json +++ b/mobile/rpc-foundation/goldens/settings-task-workspace-refused.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-task-workspace-transport-error.json b/mobile/rpc-foundation/goldens/settings-task-workspace-transport-error.json index 3dabde8d5fa..25c1677363c 100644 --- a/mobile/rpc-foundation/goldens/settings-task-workspace-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-task-workspace-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-task-write.json b/mobile/rpc-foundation/goldens/settings-task-write.json index d6ea3af2dd1..8e3b1344e80 100644 --- a/mobile/rpc-foundation/goldens/settings-task-write.json +++ b/mobile/rpc-foundation/goldens/settings-task-write.json @@ -3,7 +3,7 @@ "family": "settings-best-effort", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.json b/mobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.json index facfc0760e8..36b613e9781 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json b/mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json index 34603137d44..c96f4053db0 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-context-refused.json b/mobile/rpc-foundation/goldens/settings-workspace-context-refused.json index 1251fcfc718..9de8120f48d 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-context-refused.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-context-refused.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-context-transport-error.json b/mobile/rpc-foundation/goldens/settings-workspace-context-transport-error.json index a57ff030e7b..d74686c598c 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-context-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-context-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.json b/mobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.json index 21e6d38fe69..1e267d21844 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.workspace-submit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-submit-refused.json b/mobile/rpc-foundation/goldens/settings-workspace-submit-refused.json index ed804b3dcc1..940ae10a1d6 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-submit-refused.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-submit-refused.json @@ -3,7 +3,7 @@ "family": "settings.workspace-submit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.json b/mobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.json index c50a2e730a2..93bd4037bd9 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.workspace-submit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/speech-audio-chunk-acknowledged.json b/mobile/rpc-foundation/goldens/speech-audio-chunk-acknowledged.json index ab73718c9cf..f3485664c9e 100644 --- a/mobile/rpc-foundation/goldens/speech-audio-chunk-acknowledged.json +++ b/mobile/rpc-foundation/goldens/speech-audio-chunk-acknowledged.json @@ -3,7 +3,7 @@ "family": "speech.dictation-chunk", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-desktop-start-fulfilled.json b/mobile/rpc-foundation/goldens/speech-desktop-start-fulfilled.json index a11fdc81625..4d33e3c624b 100644 --- a/mobile/rpc-foundation/goldens/speech-desktop-start-fulfilled.json +++ b/mobile/rpc-foundation/goldens/speech-desktop-start-fulfilled.json @@ -3,7 +3,7 @@ "family": "speech.dictation-start", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-desktop-start-recording-failed.json b/mobile/rpc-foundation/goldens/speech-desktop-start-recording-failed.json index cff9f935713..f2831b7636c 100644 --- a/mobile/rpc-foundation/goldens/speech-desktop-start-recording-failed.json +++ b/mobile/rpc-foundation/goldens/speech-desktop-start-recording-failed.json @@ -3,7 +3,7 @@ "family": "speech.dictation-start", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-desktop-start-superseded.json b/mobile/rpc-foundation/goldens/speech-desktop-start-superseded.json index 57f131852c6..d02d6216dfe 100644 --- a/mobile/rpc-foundation/goldens/speech-desktop-start-superseded.json +++ b/mobile/rpc-foundation/goldens/speech-desktop-start-superseded.json @@ -3,7 +3,7 @@ "family": "speech.dictation-start", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-dictation-session-cancelled.json b/mobile/rpc-foundation/goldens/speech-dictation-session-cancelled.json index 2e488051279..d047633cb91 100644 --- a/mobile/rpc-foundation/goldens/speech-dictation-session-cancelled.json +++ b/mobile/rpc-foundation/goldens/speech-dictation-session-cancelled.json @@ -3,7 +3,7 @@ "family": "speech.dictation-session", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-dictation-session-transcript.json b/mobile/rpc-foundation/goldens/speech-dictation-session-transcript.json index cc749827b4c..0e1c06d05f8 100644 --- a/mobile/rpc-foundation/goldens/speech-dictation-session-transcript.json +++ b/mobile/rpc-foundation/goldens/speech-dictation-session-transcript.json @@ -3,7 +3,7 @@ "family": "speech.dictation-session", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-setup-sheet-denied-to-mobile.json b/mobile/rpc-foundation/goldens/speech-setup-sheet-denied-to-mobile.json index d80e188cf1a..73d8a6b1204 100644 --- a/mobile/rpc-foundation/goldens/speech-setup-sheet-denied-to-mobile.json +++ b/mobile/rpc-foundation/goldens/speech-setup-sheet-denied-to-mobile.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-setup-sheet-fulfilled.json b/mobile/rpc-foundation/goldens/speech-setup-sheet-fulfilled.json index a2ad5d1d754..d93d72fba04 100644 --- a/mobile/rpc-foundation/goldens/speech-setup-sheet-fulfilled.json +++ b/mobile/rpc-foundation/goldens/speech-setup-sheet-fulfilled.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-setup-sheet-legacy-desktop.json b/mobile/rpc-foundation/goldens/speech-setup-sheet-legacy-desktop.json index da4e4c6a1db..66295c6e144 100644 --- a/mobile/rpc-foundation/goldens/speech-setup-sheet-legacy-desktop.json +++ b/mobile/rpc-foundation/goldens/speech-setup-sheet-legacy-desktop.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-setup-sheet-model-vocabulary.json b/mobile/rpc-foundation/goldens/speech-setup-sheet-model-vocabulary.json new file mode 100644 index 00000000000..53be7509cc4 --- /dev/null +++ b/mobile/rpc-foundation/goldens/speech-setup-sheet-model-vocabulary.json @@ -0,0 +1,164 @@ +{ + "operation": "speech.setup-sheet", + "family": "speech.setup-sheet", + "namedDeltas": [], + "runnerVersion": 1, + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", + "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", + "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", + "scenarioSha256": "88e563aa087955b9ef6447b7e73711f90d5befb7c082270da58695c3a7169e67", + "platform": "darwin", + "scenarioVersion": 1, + "projectionVersion": 2, + "goldenFormatVersion": 5, + "values": { + "430a60866d83": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "dictationMode": "hold", + "enabled": true, + "models": [ + { + "id": "whisper-small", + "label": "Small", + "progress": 0.42, + "provider": "local", + "recommended": true, + "sizeBytes": 466862080, + "status": "downloading" + }, + { + "id": "gpt-4o-transcribe", + "label": "OpenAI", + "progress": { + "$rpc": "null" + }, + "provider": "openai", + "recommended": false, + "sizeBytes": { + "$rpc": "null" + }, + "status": "ready" + } + ], + "selectedModelId": "gpt-4o-transcribe" + } + }, + "4f793e38be31": { + "name": "speech.models.list#1", + "ordinal": 2, + "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"speech.models.list\",\"params\":null}" + }, + "d91e455b7326": { + "list": { + "dictationMode": "hold", + "enabled": true, + "models": [ + { + "id": "whisper-small", + "label": "Small", + "progress": 0.42, + "provider": "local", + "recommended": true, + "sizeBytes": 466862080, + "status": "downloading" + }, + { + "id": "gpt-4o-transcribe", + "label": "OpenAI", + "progress": { + "$rpc": "null" + }, + "provider": "openai", + "recommended": false, + "sizeBytes": { + "$rpc": "null" + }, + "status": "ready" + } + ], + "selectedModelId": "gpt-4o-transcribe" + } + }, + "f4bd63aa8e8f": { + "name": "speech.models.list#1", + "ordinal": 1, + "args": [ + { + "name": "method", + "value": "speech.models.list" + }, + { + "name": "params", + "value": { + "$rpc": "null" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-1", + "ok": true, + "result": { + "dictationMode": "hold", + "enabled": true, + "models": [ + { + "id": "whisper-small", + "label": "Small", + "progress": 0.42, + "provider": "local", + "recommended": true, + "sizeBytes": 466862080, + "status": "downloading" + }, + { + "id": "gpt-4o-transcribe", + "label": "OpenAI", + "progress": { + "$rpc": "null" + }, + "provider": "openai", + "recommended": false, + "sizeBytes": { + "$rpc": "null" + }, + "status": "ready" + } + ], + "selectedModelId": "gpt-4o-transcribe" + } + } + } + } + }, + "recording": { + "scenario": "speech-setup-sheet-model-vocabulary", + "checkpoints": [ + { + "id": "settled", + "observation": { + "sender": ["f4bd63aa8e8f"], + "payloads": ["4f793e38be31"], + "settlements": { + "list": "430a60866d83" + }, + "state": "d91e455b7326", + "effects": [] + } + } + ] + } +} diff --git a/mobile/rpc-foundation/goldens/structured-agent-session-created.json b/mobile/rpc-foundation/goldens/structured-agent-session-created.json index 479f7b46b55..6819729a742 100644 --- a/mobile/rpc-foundation/goldens/structured-agent-session-created.json +++ b/mobile/rpc-foundation/goldens/structured-agent-session-created.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/structured-launch-created.json b/mobile/rpc-foundation/goldens/structured-launch-created.json index 1b1f6a1d9bf..894db47ff14 100644 --- a/mobile/rpc-foundation/goldens/structured-launch-created.json +++ b/mobile/rpc-foundation/goldens/structured-launch-created.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/structured-launch-definitive-refusal.json b/mobile/rpc-foundation/goldens/structured-launch-definitive-refusal.json index 098b18e6278..236da473d8c 100644 --- a/mobile/rpc-foundation/goldens/structured-launch-definitive-refusal.json +++ b/mobile/rpc-foundation/goldens/structured-launch-definitive-refusal.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/structured-launch-replays-dropped-create.json b/mobile/rpc-foundation/goldens/structured-launch-replays-dropped-create.json index fbda5747fda..f25a46301ee 100644 --- a/mobile/rpc-foundation/goldens/structured-launch-replays-dropped-create.json +++ b/mobile/rpc-foundation/goldens/structured-launch-replays-dropped-create.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/structured-launch-support-refused.json b/mobile/rpc-foundation/goldens/structured-launch-support-refused.json index 6838f1d33e4..e5ced5c2a32 100644 --- a/mobile/rpc-foundation/goldens/structured-launch-support-refused.json +++ b/mobile/rpc-foundation/goldens/structured-launch-support-refused.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/structured-launch-unsupported.json b/mobile/rpc-foundation/goldens/structured-launch-unsupported.json index a30c2aaf617..ccf14b0dfd9 100644 --- a/mobile/rpc-foundation/goldens/structured-launch-unsupported.json +++ b/mobile/rpc-foundation/goldens/structured-launch-unsupported.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/tasks-route-repo-list.json b/mobile/rpc-foundation/goldens/tasks-route-repo-list.json index ed4581fada9..f7a50b64545 100644 --- a/mobile/rpc-foundation/goldens/tasks-route-repo-list.json +++ b/mobile/rpc-foundation/goldens/tasks-route-repo-list.json @@ -3,7 +3,7 @@ "family": "tasks.route-repo-list", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "feb6cee1ab7ecff1ba98bfba22d4924c748d3bb6b749db460cb617ee50b92f2c", diff --git a/mobile/rpc-foundation/goldens/terminal-gesture-flush-and-clear.json b/mobile/rpc-foundation/goldens/terminal-gesture-flush-and-clear.json index 17cab3d4b6e..a0346c30bc3 100644 --- a/mobile/rpc-foundation/goldens/terminal-gesture-flush-and-clear.json +++ b/mobile/rpc-foundation/goldens/terminal-gesture-flush-and-clear.json @@ -3,7 +3,7 @@ "family": "session.terminal-gesture-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9d119d5ec320e2538105d6ff673b9f4b8e3decbe46527947489dffbcf2ac0472", diff --git a/mobile/rpc-foundation/goldens/terminal-input-send-accepted.json b/mobile/rpc-foundation/goldens/terminal-input-send-accepted.json index 5b31fb2815f..f59bc417dcb 100644 --- a/mobile/rpc-foundation/goldens/terminal-input-send-accepted.json +++ b/mobile/rpc-foundation/goldens/terminal-input-send-accepted.json @@ -3,7 +3,7 @@ "family": "session.terminal-input-send", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/terminal-input-send-refused.json b/mobile/rpc-foundation/goldens/terminal-input-send-refused.json index 235bf5d8fb1..01856912c34 100644 --- a/mobile/rpc-foundation/goldens/terminal-input-send-refused.json +++ b/mobile/rpc-foundation/goldens/terminal-input-send-refused.json @@ -3,7 +3,7 @@ "family": "session.terminal-input-send", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/terminal-live-input-accepted.json b/mobile/rpc-foundation/goldens/terminal-live-input-accepted.json index bdf02eb377c..cfdef1b78ef 100644 --- a/mobile/rpc-foundation/goldens/terminal-live-input-accepted.json +++ b/mobile/rpc-foundation/goldens/terminal-live-input-accepted.json @@ -3,7 +3,7 @@ "family": "session.terminal-input-send", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/terminal-paste-accepted.json b/mobile/rpc-foundation/goldens/terminal-paste-accepted.json index 272b3f51439..19681a7278f 100644 --- a/mobile/rpc-foundation/goldens/terminal-paste-accepted.json +++ b/mobile/rpc-foundation/goldens/terminal-paste-accepted.json @@ -3,7 +3,7 @@ "family": "session.terminal-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/terminal-paste-refused.json b/mobile/rpc-foundation/goldens/terminal-paste-refused.json index 050bcbcef44..800bf304671 100644 --- a/mobile/rpc-foundation/goldens/terminal-paste-refused.json +++ b/mobile/rpc-foundation/goldens/terminal-paste-refused.json @@ -3,7 +3,7 @@ "family": "session.terminal-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/terminal-query-reply-accepted.json b/mobile/rpc-foundation/goldens/terminal-query-reply-accepted.json index 731587f7113..d5736625dd0 100644 --- a/mobile/rpc-foundation/goldens/terminal-query-reply-accepted.json +++ b/mobile/rpc-foundation/goldens/terminal-query-reply-accepted.json @@ -3,7 +3,7 @@ "family": "terminal.query-reply", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-query-reply-unsubscribed.json b/mobile/rpc-foundation/goldens/terminal-query-reply-unsubscribed.json index 808684f321a..befacc36e80 100644 --- a/mobile/rpc-foundation/goldens/terminal-query-reply-unsubscribed.json +++ b/mobile/rpc-foundation/goldens/terminal-query-reply-unsubscribed.json @@ -3,7 +3,7 @@ "family": "terminal.query-reply", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-raw-input-refused.json b/mobile/rpc-foundation/goldens/terminal-raw-input-refused.json index bab47e110d1..e6144ef92b0 100644 --- a/mobile/rpc-foundation/goldens/terminal-raw-input-refused.json +++ b/mobile/rpc-foundation/goldens/terminal-raw-input-refused.json @@ -3,7 +3,7 @@ "family": "terminal.raw-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-raw-input-reported.json b/mobile/rpc-foundation/goldens/terminal-raw-input-reported.json index 1d55c4e56f1..f0591e491be 100644 --- a/mobile/rpc-foundation/goldens/terminal-raw-input-reported.json +++ b/mobile/rpc-foundation/goldens/terminal-raw-input-reported.json @@ -3,7 +3,7 @@ "family": "terminal.raw-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-takeover-report-accepted.json b/mobile/rpc-foundation/goldens/terminal-takeover-report-accepted.json index 5361ba46a99..b274441c2b4 100644 --- a/mobile/rpc-foundation/goldens/terminal-takeover-report-accepted.json +++ b/mobile/rpc-foundation/goldens/terminal-takeover-report-accepted.json @@ -3,7 +3,7 @@ "family": "terminal.takeover-report", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-takeover-report-retried.json b/mobile/rpc-foundation/goldens/terminal-takeover-report-retried.json index d3199ba5cfe..9c1e209ecf8 100644 --- a/mobile/rpc-foundation/goldens/terminal-takeover-report-retried.json +++ b/mobile/rpc-foundation/goldens/terminal-takeover-report-retried.json @@ -3,7 +3,7 @@ "family": "terminal.takeover-report", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-viewport-refit-applied.json b/mobile/rpc-foundation/goldens/terminal-viewport-refit-applied.json index 8169cb9e5d6..9d188730755 100644 --- a/mobile/rpc-foundation/goldens/terminal-viewport-refit-applied.json +++ b/mobile/rpc-foundation/goldens/terminal-viewport-refit-applied.json @@ -3,7 +3,7 @@ "family": "terminal.viewport-refit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-viewport-refit-legacy-desktop.json b/mobile/rpc-foundation/goldens/terminal-viewport-refit-legacy-desktop.json index 84a26a94965..d82614cdd89 100644 --- a/mobile/rpc-foundation/goldens/terminal-viewport-refit-legacy-desktop.json +++ b/mobile/rpc-foundation/goldens/terminal-viewport-refit-legacy-desktop.json @@ -3,7 +3,7 @@ "family": "terminal.viewport-refit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-worktree-connection-resolved.json b/mobile/rpc-foundation/goldens/terminal-worktree-connection-resolved.json index eefe8e653a3..e5369047767 100644 --- a/mobile/rpc-foundation/goldens/terminal-worktree-connection-resolved.json +++ b/mobile/rpc-foundation/goldens/terminal-worktree-connection-resolved.json @@ -3,7 +3,7 @@ "family": "session.worktree-connection", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/tk-create-github.json b/mobile/rpc-foundation/goldens/tk-create-github.json index ae0e90a60b4..12c6382c67f 100644 --- a/mobile/rpc-foundation/goldens/tk-create-github.json +++ b/mobile/rpc-foundation/goldens/tk-create-github.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-create-gitlab.json b/mobile/rpc-foundation/goldens/tk-create-gitlab.json index 243534a05b3..0204f27b9ab 100644 --- a/mobile/rpc-foundation/goldens/tk-create-gitlab.json +++ b/mobile/rpc-foundation/goldens/tk-create-gitlab.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-create-linear.json b/mobile/rpc-foundation/goldens/tk-create-linear.json index d4915627585..840fc84b802 100644 --- a/mobile/rpc-foundation/goldens/tk-create-linear.json +++ b/mobile/rpc-foundation/goldens/tk-create-linear.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-item-checks-files.json b/mobile/rpc-foundation/goldens/tk-item-checks-files.json index d8cc425c451..96f0e05cb2c 100644 --- a/mobile/rpc-foundation/goldens/tk-item-checks-files.json +++ b/mobile/rpc-foundation/goldens/tk-item-checks-files.json @@ -3,7 +3,7 @@ "family": "tasks.item-checks-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/tk-item-comment-github.json b/mobile/rpc-foundation/goldens/tk-item-comment-github.json index 16eb40a8578..602c974130f 100644 --- a/mobile/rpc-foundation/goldens/tk-item-comment-github.json +++ b/mobile/rpc-foundation/goldens/tk-item-comment-github.json @@ -3,7 +3,7 @@ "family": "tasks.item-comment-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/tk-item-comment-gitlab-mr.json b/mobile/rpc-foundation/goldens/tk-item-comment-gitlab-mr.json index 86fea90e25b..f8c3b17dc35 100644 --- a/mobile/rpc-foundation/goldens/tk-item-comment-gitlab-mr.json +++ b/mobile/rpc-foundation/goldens/tk-item-comment-gitlab-mr.json @@ -3,7 +3,7 @@ "family": "tasks.item-comment-gitlab-mr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/tk-item-comment-gitlab.json b/mobile/rpc-foundation/goldens/tk-item-comment-gitlab.json index 9eadbcd9e81..c36a59ddca8 100644 --- a/mobile/rpc-foundation/goldens/tk-item-comment-gitlab.json +++ b/mobile/rpc-foundation/goldens/tk-item-comment-gitlab.json @@ -3,7 +3,7 @@ "family": "tasks.item-comment-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/tk-item-detail-github-reactions.json b/mobile/rpc-foundation/goldens/tk-item-detail-github-reactions.json index ee1b8a89283..4d5b7067c5d 100644 --- a/mobile/rpc-foundation/goldens/tk-item-detail-github-reactions.json +++ b/mobile/rpc-foundation/goldens/tk-item-detail-github-reactions.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/tk-item-detail-github.json b/mobile/rpc-foundation/goldens/tk-item-detail-github.json index 79545254784..1ea99b3c0aa 100644 --- a/mobile/rpc-foundation/goldens/tk-item-detail-github.json +++ b/mobile/rpc-foundation/goldens/tk-item-detail-github.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/tk-item-detail-gitlab-reactions.json b/mobile/rpc-foundation/goldens/tk-item-detail-gitlab-reactions.json index 7d3cabc8655..30bb82d10bb 100644 --- a/mobile/rpc-foundation/goldens/tk-item-detail-gitlab-reactions.json +++ b/mobile/rpc-foundation/goldens/tk-item-detail-gitlab-reactions.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/tk-item-detail-gitlab.json b/mobile/rpc-foundation/goldens/tk-item-detail-gitlab.json index 7456862ac23..16fc27a5e93 100644 --- a/mobile/rpc-foundation/goldens/tk-item-detail-gitlab.json +++ b/mobile/rpc-foundation/goldens/tk-item-detail-gitlab.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/tk-item-detail-linear.json b/mobile/rpc-foundation/goldens/tk-item-detail-linear.json index a499e51a0eb..6c373d1186d 100644 --- a/mobile/rpc-foundation/goldens/tk-item-detail-linear.json +++ b/mobile/rpc-foundation/goldens/tk-item-detail-linear.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/tk-item-detail-metadata.json b/mobile/rpc-foundation/goldens/tk-item-detail-metadata.json index 7fb5d785a8d..b3af1259c82 100644 --- a/mobile/rpc-foundation/goldens/tk-item-detail-metadata.json +++ b/mobile/rpc-foundation/goldens/tk-item-detail-metadata.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "58ea1553e04017c993aea4753aace41ee664705a3fdb3b18569c5a9d7968cf06", diff --git a/mobile/rpc-foundation/goldens/tk-item-merge-gitlab.json b/mobile/rpc-foundation/goldens/tk-item-merge-gitlab.json index 7e63fc0769b..fa99d7d3d72 100644 --- a/mobile/rpc-foundation/goldens/tk-item-merge-gitlab.json +++ b/mobile/rpc-foundation/goldens/tk-item-merge-gitlab.json @@ -3,7 +3,7 @@ "family": "tasks.item-merge-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/tk-item-metadata-github.json b/mobile/rpc-foundation/goldens/tk-item-metadata-github.json index 43838f19f02..dd2efda3fbf 100644 --- a/mobile/rpc-foundation/goldens/tk-item-metadata-github.json +++ b/mobile/rpc-foundation/goldens/tk-item-metadata-github.json @@ -3,7 +3,7 @@ "family": "tasks.item-metadata-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab-mr.json b/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab-mr.json index ad7b7d57786..e38436d89b3 100644 --- a/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab-mr.json +++ b/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab-mr.json @@ -3,7 +3,7 @@ "family": "tasks.item-metadata-gitlab-mr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab.json b/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab.json index b13c5eaea50..f783f2bf635 100644 --- a/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab.json +++ b/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab.json @@ -3,7 +3,7 @@ "family": "tasks.item-metadata-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/tk-item-reply-merge.json b/mobile/rpc-foundation/goldens/tk-item-reply-merge.json index 751f3787968..78030dba264 100644 --- a/mobile/rpc-foundation/goldens/tk-item-reply-merge.json +++ b/mobile/rpc-foundation/goldens/tk-item-reply-merge.json @@ -3,7 +3,7 @@ "family": "tasks.item-reply-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/tk-item-review-github.json b/mobile/rpc-foundation/goldens/tk-item-review-github.json index 8c133390e85..b1192ff8fb1 100644 --- a/mobile/rpc-foundation/goldens/tk-item-review-github.json +++ b/mobile/rpc-foundation/goldens/tk-item-review-github.json @@ -3,7 +3,7 @@ "family": "tasks.item-review-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/tk-item-status-gitlab-mr.json b/mobile/rpc-foundation/goldens/tk-item-status-gitlab-mr.json index b6f0aacbfd9..d1f463a9994 100644 --- a/mobile/rpc-foundation/goldens/tk-item-status-gitlab-mr.json +++ b/mobile/rpc-foundation/goldens/tk-item-status-gitlab-mr.json @@ -3,7 +3,7 @@ "family": "tasks.item-status-gitlab-mr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/tk-item-status-gitlab.json b/mobile/rpc-foundation/goldens/tk-item-status-gitlab.json index b0a2d3da129..05bb0387600 100644 --- a/mobile/rpc-foundation/goldens/tk-item-status-gitlab.json +++ b/mobile/rpc-foundation/goldens/tk-item-status-gitlab.json @@ -3,7 +3,7 @@ "family": "tasks.item-status-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/tk-linear-connect.json b/mobile/rpc-foundation/goldens/tk-linear-connect.json index 917a05af026..5ad3af0b71b 100644 --- a/mobile/rpc-foundation/goldens/tk-linear-connect.json +++ b/mobile/rpc-foundation/goldens/tk-linear-connect.json @@ -3,7 +3,7 @@ "family": "tasks.linear-connect", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-linear-item.json b/mobile/rpc-foundation/goldens/tk-linear-item.json index 853dea595cc..cc9bbf5030d 100644 --- a/mobile/rpc-foundation/goldens/tk-linear-item.json +++ b/mobile/rpc-foundation/goldens/tk-linear-item.json @@ -3,7 +3,7 @@ "family": "tasks.linear-item", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/tk-linear-team-context.json b/mobile/rpc-foundation/goldens/tk-linear-team-context.json index 6e059219359..496ec532e4b 100644 --- a/mobile/rpc-foundation/goldens/tk-linear-team-context.json +++ b/mobile/rpc-foundation/goldens/tk-linear-team-context.json @@ -3,7 +3,7 @@ "family": "tasks.linear-team-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "58ea1553e04017c993aea4753aace41ee664705a3fdb3b18569c5a9d7968cf06", diff --git a/mobile/rpc-foundation/goldens/tk-list-gitlab-items.json b/mobile/rpc-foundation/goldens/tk-list-gitlab-items.json index a357474a6ef..780952b3a8e 100644 --- a/mobile/rpc-foundation/goldens/tk-list-gitlab-items.json +++ b/mobile/rpc-foundation/goldens/tk-list-gitlab-items.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-gitlab-items", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-list-gitlab-todos.json b/mobile/rpc-foundation/goldens/tk-list-gitlab-todos.json index 59677e8af9e..7b08c3b82dd 100644 --- a/mobile/rpc-foundation/goldens/tk-list-gitlab-todos.json +++ b/mobile/rpc-foundation/goldens/tk-list-gitlab-todos.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-gitlab-todos", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-list-linear.json b/mobile/rpc-foundation/goldens/tk-list-linear.json index 1a0982a4616..b864dea7f23 100644 --- a/mobile/rpc-foundation/goldens/tk-list-linear.json +++ b/mobile/rpc-foundation/goldens/tk-list-linear.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-project-board-load.json b/mobile/rpc-foundation/goldens/tk-project-board-load.json index 09d70c53c32..444f0417abb 100644 --- a/mobile/rpc-foundation/goldens/tk-project-board-load.json +++ b/mobile/rpc-foundation/goldens/tk-project-board-load.json @@ -3,7 +3,7 @@ "family": "tasks.project-board-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/tk-project-repo-slugs.json b/mobile/rpc-foundation/goldens/tk-project-repo-slugs.json index 187b93bd340..33beadc63be 100644 --- a/mobile/rpc-foundation/goldens/tk-project-repo-slugs.json +++ b/mobile/rpc-foundation/goldens/tk-project-repo-slugs.json @@ -3,7 +3,7 @@ "family": "tasks.project-repo-slugs", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-comments-issue.json b/mobile/rpc-foundation/goldens/tk-project-row-comments-issue.json index 9734b1b192c..39d021edf51 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-comments-issue.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-comments-issue.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-comments-issue", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-comments-pr.json b/mobile/rpc-foundation/goldens/tk-project-row-comments-pr.json index 26a1e023877..18c903f9b78 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-comments-pr.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-comments-pr.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-comments-pr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-detail.json b/mobile/rpc-foundation/goldens/tk-project-row-detail.json index 7ba4700f58b..44013321640 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-detail.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-detail.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-detail", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f8f6e5d500f959b9b15c5498885a05422747880b6aef4ad795bc3064ebbacea6", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-fields.json b/mobile/rpc-foundation/goldens/tk-project-row-fields.json index cc90b1e5c69..da7e022518e 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-fields.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-fields.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-fields", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-files-merge.json b/mobile/rpc-foundation/goldens/tk-project-row-files-merge.json index 933c00d6683..d88823231c4 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-files-merge.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-files-merge.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-files-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b228732762828412ad3d9eec3ece00a897d866046e37044322c3911758d6e0a9", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-metadata-load.json b/mobile/rpc-foundation/goldens/tk-project-row-metadata-load.json index 8b3d11354d3..0696ec08cda 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-metadata-load.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-metadata-load.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-metadata-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f8f6e5d500f959b9b15c5498885a05422747880b6aef4ad795bc3064ebbacea6", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-review-checks.json b/mobile/rpc-foundation/goldens/tk-project-row-review-checks.json index 4f6440307e7..d1886375eb1 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-review-checks.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-review-checks.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-review-checks", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-threads.json b/mobile/rpc-foundation/goldens/tk-project-row-threads.json index 48860ada04c..46a7b6d014e 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-threads.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-threads.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-threads", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/tk-provider-load.json b/mobile/rpc-foundation/goldens/tk-provider-load.json index 40fd65f3a6c..3869305b568 100644 --- a/mobile/rpc-foundation/goldens/tk-provider-load.json +++ b/mobile/rpc-foundation/goldens/tk-provider-load.json @@ -3,7 +3,7 @@ "family": "tasks.provider-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.json b/mobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.json index 8dc2d37229c..f0e434ddcf2 100644 --- a/mobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.json +++ b/mobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.json @@ -3,7 +3,7 @@ "family": "transport.capability-probe", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.json b/mobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.json index 65fc3816637..e2ed9f98cd7 100644 --- a/mobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.json +++ b/mobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.json @@ -3,7 +3,7 @@ "family": "transport.capability-probe", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-capability-probe-publishes.json b/mobile/rpc-foundation/goldens/transport-capability-probe-publishes.json index cbbc6737dbe..109e2a79641 100644 --- a/mobile/rpc-foundation/goldens/transport-capability-probe-publishes.json +++ b/mobile/rpc-foundation/goldens/transport-capability-probe-publishes.json @@ -3,7 +3,7 @@ "family": "transport.capability-probe", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.json b/mobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.json index e4b77987fb6..3997f6cd60f 100644 --- a/mobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.json +++ b/mobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.json @@ -3,7 +3,7 @@ "family": "transport.capability-probe", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.json b/mobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.json index eb62ab108d3..5fe7219d16d 100644 --- a/mobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.json +++ b/mobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.json @@ -3,7 +3,7 @@ "family": "transport.host-status-gates", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-host-status-gates-ready.json b/mobile/rpc-foundation/goldens/transport-host-status-gates-ready.json index 49d853418fb..4705d6824b1 100644 --- a/mobile/rpc-foundation/goldens/transport-host-status-gates-ready.json +++ b/mobile/rpc-foundation/goldens/transport-host-status-gates-ready.json @@ -3,7 +3,7 @@ "family": "transport.host-status-gates", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.json b/mobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.json index 84a3ec14056..4ad64f32483 100644 --- a/mobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.json +++ b/mobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.json @@ -3,7 +3,7 @@ "family": "transport.host-status-gates", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-pairing-race-both-refused.json b/mobile/rpc-foundation/goldens/transport-pairing-race-both-refused.json index fe698a7d03b..011c7da798e 100644 --- a/mobile/rpc-foundation/goldens/transport-pairing-race-both-refused.json +++ b/mobile/rpc-foundation/goldens/transport-pairing-race-both-refused.json @@ -3,7 +3,7 @@ "family": "transport.pairing-race", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-pairing-race-direct-completes-first.json b/mobile/rpc-foundation/goldens/transport-pairing-race-direct-completes-first.json index 74c86d12c6a..beab4beb7ba 100644 --- a/mobile/rpc-foundation/goldens/transport-pairing-race-direct-completes-first.json +++ b/mobile/rpc-foundation/goldens/transport-pairing-race-direct-completes-first.json @@ -3,7 +3,7 @@ "family": "transport.pairing-race", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-pairing-race-relay-completes-first.json b/mobile/rpc-foundation/goldens/transport-pairing-race-relay-completes-first.json index 9f52840746b..9c08cd39610 100644 --- a/mobile/rpc-foundation/goldens/transport-pairing-race-relay-completes-first.json +++ b/mobile/rpc-foundation/goldens/transport-pairing-race-relay-completes-first.json @@ -3,7 +3,7 @@ "family": "transport.pairing-race", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.json b/mobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.json index 833efdd49d9..094f51dcc06 100644 --- a/mobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.json +++ b/mobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.json @@ -3,7 +3,7 @@ "family": "transport.pairing-race", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/tw-capabilities-advertised.json b/mobile/rpc-foundation/goldens/tw-capabilities-advertised.json index 005392b53c6..a33899c53e6 100644 --- a/mobile/rpc-foundation/goldens/tw-capabilities-advertised.json +++ b/mobile/rpc-foundation/goldens/tw-capabilities-advertised.json @@ -3,7 +3,7 @@ "family": "worktree.runtime-capabilities", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.json b/mobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.json index 71c33439e52..ad78a98b67b 100644 --- a/mobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.json +++ b/mobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.json @@ -3,7 +3,7 @@ "family": "worktree.runtime-capabilities", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.json b/mobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.json index 9fd619c4074..e29031997a7 100644 --- a/mobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.json +++ b/mobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.json @@ -3,7 +3,7 @@ "family": "worktree.runtime-capabilities", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-agent-launched.json b/mobile/rpc-foundation/goldens/tw-create-retry-agent-launched.json index c38a625b3ed..43cf2c17bb1 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-agent-launched.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-agent-launched.json @@ -3,7 +3,7 @@ "family": "worktree.agent-launch-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.json b/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.json index 504da80c6a0..be4dbd35608 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.json b/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.json index 0741a4ba24b..1de6757edf1 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json b/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json index 19497ae72c9..ea7fd5ef817 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-created.json b/mobile/rpc-foundation/goldens/tw-create-retry-created.json index f5eda2bce88..fae638b1b80 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-created.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-created.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-name-collision.json b/mobile/rpc-foundation/goldens/tw-create-retry-name-collision.json index c16c16b395f..c7495cdcad9 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-name-collision.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-name-collision.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.json b/mobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.json index 0817c4d7d29..6765290d15a 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-warning-kept.json b/mobile/rpc-foundation/goldens/tw-create-retry-warning-kept.json index ae93a58f838..3e2e47dc2ab 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-warning-kept.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-warning-kept.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json b/mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json index fda4c1d2147..524d18d6db3 100644 --- a/mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json +++ b/mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json @@ -3,7 +3,7 @@ "family": "worktree.hosted-base", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json b/mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json index e0dfd184ad0..2aa4754d638 100644 --- a/mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json +++ b/mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json @@ -3,7 +3,7 @@ "family": "worktree.hosted-base", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-paste-lookup-resolved.json b/mobile/rpc-foundation/goldens/tw-paste-lookup-resolved.json index a394d20ff47..0a31bbeb5b0 100644 --- a/mobile/rpc-foundation/goldens/tw-paste-lookup-resolved.json +++ b/mobile/rpc-foundation/goldens/tw-paste-lookup-resolved.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json b/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json index a3bafc37f69..d397f20748b 100644 --- a/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json +++ b/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.json b/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.json index 890199612c6..1c88143ff04 100644 --- a/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.json +++ b/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json b/mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json index e4aba088ecf..3516410a805 100644 --- a/mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json +++ b/mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json @@ -3,7 +3,7 @@ "family": "worktree.setup-hook-trust", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json b/mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json index 578f6c009d9..4cc3e4a49bf 100644 --- a/mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json +++ b/mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json @@ -3,7 +3,7 @@ "family": "worktree.setup-hook-trust", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-smart-search-all-providers.json b/mobile/rpc-foundation/goldens/tw-smart-search-all-providers.json index ad774256225..05aaf02f13b 100644 --- a/mobile/rpc-foundation/goldens/tw-smart-search-all-providers.json +++ b/mobile/rpc-foundation/goldens/tw-smart-search-all-providers.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json b/mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json index 643ac585eec..1098d06e723 100644 --- a/mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json +++ b/mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-smart-search-linear-listed.json b/mobile/rpc-foundation/goldens/tw-smart-search-linear-listed.json index 4af6641b24a..2c5a3f2c903 100644 --- a/mobile/rpc-foundation/goldens/tw-smart-search-linear-listed.json +++ b/mobile/rpc-foundation/goldens/tw-smart-search-linear-listed.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json b/mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json index b2f3dc41be0..2a9e45a155a 100644 --- a/mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json +++ b/mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json @@ -3,7 +3,7 @@ "family": "settings-best-effort", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.json b/mobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.json index 96435ebc76d..c7ad2ad4837 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-source", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-source-presets.json b/mobile/rpc-foundation/goldens/tw-workspace-source-presets.json index 0670fa77476..7b49600d281 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-source-presets.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-source-presets.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-source", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.json b/mobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.json index 3877213a8c3..d01278e3fa7 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-sparse", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json b/mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json index 76f26fcd80c..9478be009c0 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-sparse", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.json b/mobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.json index d4124bd9e64..1ffc263966d 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json b/mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json index 7e18f38a1c5..94b921632ed 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.json b/mobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.json index 072c5a1103a..9df1fda8bf9 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh-local", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json b/mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json index 159b10c78ce..09db37826a6 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/worktree-catalog-snapshot-unreadable.json b/mobile/rpc-foundation/goldens/worktree-catalog-snapshot-unreadable.json index 8f3a28547eb..bd56ef6ec3d 100644 --- a/mobile/rpc-foundation/goldens/worktree-catalog-snapshot-unreadable.json +++ b/mobile/rpc-foundation/goldens/worktree-catalog-snapshot-unreadable.json @@ -3,7 +3,7 @@ "family": "worktree.catalog-snapshot", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/goldens/worktree-catalog-snapshot.json b/mobile/rpc-foundation/goldens/worktree-catalog-snapshot.json index c1ac6642467..688aff036e7 100644 --- a/mobile/rpc-foundation/goldens/worktree-catalog-snapshot.json +++ b/mobile/rpc-foundation/goldens/worktree-catalog-snapshot.json @@ -3,7 +3,7 @@ "family": "worktree.catalog-snapshot", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/goldens/worktree-home-catalog.json b/mobile/rpc-foundation/goldens/worktree-home-catalog.json index 22bb0ceee22..3316607aa91 100644 --- a/mobile/rpc-foundation/goldens/worktree-home-catalog.json +++ b/mobile/rpc-foundation/goldens/worktree-home-catalog.json @@ -3,7 +3,7 @@ "family": "worktree.home-catalog", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/goldens/worktree-retired-names.json b/mobile/rpc-foundation/goldens/worktree-retired-names.json index 676c6e15c34..06e8cb8dd9b 100644 --- a/mobile/rpc-foundation/goldens/worktree-retired-names.json +++ b/mobile/rpc-foundation/goldens/worktree-retired-names.json @@ -3,7 +3,7 @@ "family": "worktree.retired-names", "namedDeltas": [], "runnerVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/pilot-scenarios.json b/mobile/rpc-foundation/pilot-scenarios.json index 067c4073ba4..9e3e384e7d3 100644 --- a/mobile/rpc-foundation/pilot-scenarios.json +++ b/mobile/rpc-foundation/pilot-scenarios.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "baseline": "6142657d7ad4af81cb4449176048edadef10db45", + "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", "scenarios": [ { "id": "b1", @@ -1178,6 +1178,118 @@ } ] }, + { + "id": "settings-repo-metadata-icons", + "operation": "settings.repo-metadata", + "version": 1, + "family": "settings.repo-metadata", + "sites": ["mobile/src/host-screen/use-host-repo-metadata.ts"], + "schedules": ["settings-first"], + "steps": [ + { + "action": "mount", + "id": "mount" + }, + { + "action": "load", + "id": "load" + }, + { + "complete": "repo.list#1", + "params": { + "$undefined": true + }, + "reply": { + "ok": true, + "result": { + "repos": [ + { + "id": "repo-1", + "displayName": "Lucide", + "badgeColor": "#123456", + "repoIcon": { + "type": "lucide", + "name": "Rocket" + }, + "connectionId": null + }, + { + "id": "repo-2", + "displayName": "Emoji", + "repoIcon": { + "type": "emoji", + "emoji": "🐳" + }, + "connectionId": "ssh-1" + }, + { + "id": "repo-3", + "displayName": "Avatar", + "repoIcon": { + "type": "image", + "src": "https://github.com/acme.png?size=64", + "source": "github", + "label": "acme/orca" + }, + "executionHostId": "ssh:ssh-1" + } + ] + } + } + }, + { + "complete": "ssh.listTargetSummaries#1", + "params": { + "$undefined": true + }, + "reply": { + "ok": true, + "result": { + "targets": [ + { + "id": "ssh-1", + "label": "SSH" + } + ] + } + } + }, + { + "checkpoint": "settings-pending" + }, + { + "complete": "settings.get#1", + "params": { + "$undefined": true + }, + "reply": { + "ok": true, + "result": { + "settings": { + "defaultTuiAgent": "codex", + "disabledTuiAgents": [], + "hostSettingOverrides": {} + } + } + } + }, + { + "complete": "host.platform#1", + "params": { + "$undefined": true + }, + "reply": { + "ok": true, + "result": { + "platform": "darwin" + } + } + }, + { + "checkpoint": "settled" + } + ] + }, { "id": "settings-repo-metadata-refused", "operation": "settings.repo-metadata", @@ -14039,6 +14151,55 @@ } ] }, + { + "id": "speech-setup-sheet-model-vocabulary", + "operation": "speech.setup-sheet", + "version": 1, + "family": "speech.setup-sheet", + "sites": ["mobile/src/dictation/mobile-dictation-setup.ts"], + "schedules": [], + "steps": [ + { + "action": "list", + "id": "list" + }, + { + "complete": "speech.models.list#1", + "params": null, + "reply": { + "ok": true, + "result": { + "enabled": true, + "selectedModelId": "gpt-4o-transcribe", + "dictationMode": "hold", + "models": [ + { + "id": "whisper-small", + "label": "Small", + "provider": "local", + "status": "downloading", + "sizeBytes": 466862080, + "progress": 0.42, + "recommended": true + }, + { + "id": "gpt-4o-transcribe", + "label": "OpenAI", + "provider": "openai", + "status": "ready", + "sizeBytes": null, + "progress": null, + "recommended": false + } + ] + } + } + }, + { + "checkpoint": "settled" + } + ] + }, { "id": "speech-setup-sheet-legacy-desktop", "operation": "speech.setup-sheet", @@ -22935,6 +23096,190 @@ "checkpoint": "settled" } ] + }, + { + "id": "files-preview-worktree-text-read", + "operation": "files.preview-load", + "version": 1, + "family": "files.preview-worktree-text", + "sites": ["mobile/src/files/mobile-file-preview-request.ts"], + "schedules": [], + "steps": [ + { + "action": "worktree", + "id": "load" + }, + { + "complete": "files.read#1", + "params": { + "worktree": "id:workspace-1", + "relativePath": "docs/readme.md" + }, + "reply": { + "ok": true, + "result": { + "content": "# readme", + "truncated": false, + "byteLength": 8 + } + } + }, + { + "checkpoint": "settled" + } + ] + }, + { + "id": "files-preview-worktree-image-read", + "operation": "files.preview-load", + "version": 1, + "family": "files.preview-worktree-image", + "sites": ["mobile/src/files/mobile-file-preview-request.ts"], + "schedules": [], + "steps": [ + { + "action": "worktree", + "id": "load", + "args": { + "path": "docs/logo.png" + } + }, + { + "complete": "files.readPreview#1", + "params": { + "worktree": "id:workspace-1", + "relativePath": "docs/logo.png" + }, + "reply": { + "ok": true, + "result": { + "content": "aGk=", + "isBinary": true, + "isImage": true, + "mimeType": "image/png" + } + } + }, + { + "checkpoint": "settled" + } + ] + }, + { + "id": "files-preview-artifact-image-read", + "operation": "files.preview-load", + "version": 1, + "family": "files.preview-artifact-image", + "sites": ["mobile/src/files/mobile-file-preview-request.ts"], + "schedules": [], + "steps": [ + { + "action": "artifact", + "id": "load", + "args": { + "path": "/logs/shot.png" + } + }, + { + "complete": "files.readTerminalArtifactPreview#1", + "params": { + "worktree": "id:workspace-1", + "absolutePath": "/logs/shot.png", + "grantId": "grant-1" + }, + "reply": { + "ok": true, + "result": { + "content": "aGk=", + "isBinary": true, + "isImage": true, + "mimeType": "image/png" + } + } + }, + { + "checkpoint": "settled" + } + ] + }, + { + "id": "session-markdown-disk-read", + "operation": "session.tab-documents", + "version": 1, + "family": "session.markdown-disk-fallback", + "sites": ["mobile/src/session/use-mobile-session-document-readers.ts"], + "schedules": [], + "steps": [ + { + "action": "markdown", + "id": "markdown" + }, + { + "complete": "markdown.readTab#1", + "params": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + }, + "reply": { + "ok": false, + "error": { + "code": "renderer_unavailable", + "message": "Renderer unavailable" + } + } + }, + { + "complete": "files.read#1", + "params": { + "relativePath": "docs/readme.md", + "worktree": "id:workspace-1" + }, + "reply": { + "ok": true, + "result": { + "content": "# disk", + "truncated": false, + "byteLength": 6 + } + } + }, + { + "checkpoint": "fell-back" + } + ] + }, + { + "id": "session-markdown-disk-served", + "operation": "session.tab-documents", + "version": 1, + "family": "session.markdown-disk-fallback", + "sites": ["mobile/src/session/use-mobile-session-document-readers.ts"], + "schedules": [], + "steps": [ + { + "action": "markdown", + "id": "markdown" + }, + { + "complete": "markdown.readTab#1", + "params": { + "tabId": "tab-md", + "worktree": "id:workspace-1" + }, + "reply": { + "ok": true, + "result": { + "content": "# served", + "version": "v1", + "isDirty": false, + "editable": true + } + } + }, + { + "checkpoint": "served" + } + ] } ] } diff --git a/mobile/src/agent-history/agent-history-reply-schema.test.ts b/mobile/src/agent-history/agent-history-reply-schema.test.ts new file mode 100644 index 00000000000..35eb9999713 --- /dev/null +++ b/mobile/src/agent-history/agent-history-reply-schema.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, it } from 'vitest' +import { + agentHistoryHostStatusSchema, + agentHistorySessionScanSchema, + resumeMetadataListSchema, + resumeRepoListSchema +} from './agent-history-reply-schema' + +describe('agent history reply schemas', () => { + it('reads an unreadable capability list as a host that does not advertise the vault', () => { + expect(agentHistoryHostStatusSchema.parse({}).capabilities).toBeUndefined() + expect(agentHistoryHostStatusSchema.parse({ capabilities: 'all' }).capabilities).toBeUndefined() + expect(agentHistoryHostStatusSchema.parse({ capabilities: ['x'] }).capabilities).toEqual(['x']) + }) + + it('passes the rest of the status record through for the platform readers', () => { + expect( + agentHistoryHostStatusSchema.parse({ capabilities: [], platform: 'win32' }) + ).toMatchObject({ platform: 'win32' }) + }) + + it('requires both scan containers the ready screen publishes', () => { + expect(agentHistorySessionScanSchema.safeParse({ sessions: [] }).success).toBe(false) + expect(agentHistorySessionScanSchema.safeParse({ issues: [] }).success).toBe(false) + expect(agentHistorySessionScanSchema.safeParse({ sessions: 'none', issues: [] }).success).toBe( + false + ) + expect(agentHistorySessionScanSchema.safeParse({ sessions: [], issues: [] }).success).toBe(true) + }) + + it('keeps a session row whose agent this build has never heard of', () => { + // The agent vocabulary grows with every CLI Orca learns to scan and is echoed back on resume, + // so a newer host's rows must survive rather than being refused or dropped. + const parsed = agentHistorySessionScanSchema.parse({ + sessions: [{ id: 's1', agent: 'some-new-agent' }], + issues: [] + }) + expect(parsed.sessions).toEqual([{ id: 's1', agent: 'some-new-agent' }]) + }) + + it('requires the resume repo list to be an array', () => { + expect(resumeRepoListSchema.safeParse({}).success).toBe(false) + expect(resumeRepoListSchema.safeParse({ repos: 'none' }).success).toBe(false) + expect(resumeRepoListSchema.parse({ repos: [{ id: 'r' }] }).repos).toEqual([{ id: 'r' }]) + }) + + it('accepts any resume metadata object and refuses a payload that is not one', () => { + expect(resumeMetadataListSchema.parse({ folderWorkspaces: [] })).toMatchObject({ + folderWorkspaces: [] + }) + expect(resumeMetadataListSchema.safeParse('none').success).toBe(false) + expect(resumeMetadataListSchema.safeParse(null).success).toBe(false) + }) +}) diff --git a/mobile/src/agent-history/agent-history-reply-schema.ts b/mobile/src/agent-history/agent-history-reply-schema.ts new file mode 100644 index 00000000000..d0feed40ea8 --- /dev/null +++ b/mobile/src/agent-history/agent-history-reply-schema.ts @@ -0,0 +1,66 @@ +import { z } from 'zod' +import { salvagedOptional } from '../../../src/shared/zod-salvage' + +// The agent-history screen's own reads and the workspace metadata its resume sheet loads. +// Checked against src/main/runtime/rpc/methods/status.ts:6, ai-vault.ts:66, repo.ts:29/42, +// folder-workspace.ts:12 and worktree-catalog-methods.ts:12, and the shared records they return: +// AiVaultSession and AiVaultScanIssue in src/shared/ai-vault-types.ts. + +/** + * The capability list the screen gates the whole surface on. + * + * `capabilities` sits behind main's own `?.includes` and stays salvaged: a reply without a readable + * list reads as "this host does not advertise the vault", which is the `unsupported` screen main + * already showed for it. Individual entries are not narrowed — the list is a growing vocabulary and + * every read is an `includes` against one known token. + * + * The status payload itself is otherwise passed through: the panel hands the whole record to + * readMobileRuntimeHostPlatform and readMobileRuntimeTerminalWindowsShell, both total guards over + * `unknown`, so re-declaring what they read here would narrow two members this screen never + * destructures. + */ +export const agentHistoryHostStatusSchema = z.looseObject({ + capabilities: salvagedOptional('capabilities', z.array(z.string())) +}) + +/** + * The session scan. + * + * Both members are required arrays: use-mobile-agent-history-state.ts:133-135 publishes them straight + * into the ready screen state, where the list maps `sessions` and the issue banner counts `issues` + * — a reply missing either left the screen `ready` over an undefined container and crashed on the + * next render, which is the defect this reader exists to name. + * + * The rows stay unknown, and that is deliberate rather than unfinished. A row is an AiVaultSession, + * whose `agent` is a 21-arm vocabulary that grows with every agent CLI Orca learns to scan — and + * which this client echoes straight back to the host when it resumes a session. Declaring it would + * either refuse a newer host's whole reply or silently drop the very sessions that host added, and + * the remote-wire contract is explicit that a member a client sends back passes through as the host + * wrote it rather than through a client-side fallback. + */ +export const agentHistorySessionScanSchema = z.looseObject({ + sessions: z.array(z.unknown()), + issues: z.array(z.unknown()) +}) + +/** + * The repo identities the resume sheet resolves a session's workspace through. + * + * `repos` is required and an array: loadMobileResumeMetadata reads the member off the payload and + * only then falls back to `[]`, so a null result was a property read on null at the return + * statement and a non-array reached every `repos.find` as one. The rows stay unknown for the same + * reason the session rows do — `executionHostId` is a host-id spelling the resolver already + * degrades, and closing it here would refuse a newer host's own catalog. + */ +export const resumeRepoListSchema = z.looseObject({ repos: z.array(z.unknown()) }) + +/** + * The folder workspaces, project groups and worktrees the resume sheet enriches a target with. + * + * All three are skips whose member read stays at the call site behind `readAcceptedResumeList`, + * which answers `undefined` for anything it cannot read and lets each list degrade to empty. Only + * the container is checked here, because that is the one thing main did not check: a bare string + * reply reached `?.[key]` as `undefined` and the sheet resolved every target to `unknown` with no + * sign anything had gone wrong. + */ +export const resumeMetadataListSchema = z.looseObject({}) diff --git a/mobile/src/agent-history/mobile-agent-history-operations.ts b/mobile/src/agent-history/mobile-agent-history-operations.ts index 7e10f050278..a46a0d1d05b 100644 --- a/mobile/src/agent-history/mobile-agent-history-operations.ts +++ b/mobile/src/agent-history/mobile-agent-history-operations.ts @@ -1,5 +1,11 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc-operation' -import { rpcUncheckedPayloadReader } from '../transport/rpc-reader-payload' +import { rpcResultVariant } from '../transport/rpc-operation-result-reader' +import { + agentHistoryHostStatusSchema, + agentHistorySessionScanSchema, + resumeMetadataListSchema, + resumeRepoListSchema +} from './agent-history-reply-schema' // The agent-history screen's own reads: the capability gate and session scan it runs on open, and // the workspace metadata the resume sheet loads once the user asks to resume a session. @@ -16,7 +22,7 @@ export const agentHistoryHostStatusRead = bindDeferredRpcOperation( method: 'status.get', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('host-status') + read: rpcResultVariant('host-status', agentHistoryHostStatusSchema) }) ) @@ -26,7 +32,7 @@ export const agentHistorySessionScan = bindDeferredRpcOperation( method: 'aiVault.listSessions', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('agent-sessions') + read: rpcResultVariant('agent-sessions', agentHistorySessionScanSchema) }) ) @@ -42,7 +48,7 @@ export const resumeRepoListRead = bindDeferredRpcOperation( method: 'repo.list', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('resume-repos') + read: rpcResultVariant('resume-repos', resumeRepoListSchema) }) ) @@ -56,7 +62,7 @@ export const resumeFolderWorkspaceListRead = bindDeferredRpcOperation( method: 'folderWorkspace.list', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('resume-folder-workspaces') + read: rpcResultVariant('resume-folder-workspaces', resumeMetadataListSchema) }) ) @@ -66,7 +72,7 @@ export const resumeProjectGroupListRead = bindDeferredRpcOperation( method: 'projectGroup.list', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('resume-project-groups') + read: rpcResultVariant('resume-project-groups', resumeMetadataListSchema) }) ) @@ -76,6 +82,6 @@ export const resumeWorktreeListRead = bindDeferredRpcOperation( method: 'worktree.ps', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('resume-worktrees') + read: rpcResultVariant('resume-worktrees', resumeMetadataListSchema) }) ) diff --git a/mobile/src/agent-history/use-mobile-agent-history-state.ts b/mobile/src/agent-history/use-mobile-agent-history-state.ts index d79eb89937b..5eea3cfdb62 100644 --- a/mobile/src/agent-history/use-mobile-agent-history-state.ts +++ b/mobile/src/agent-history/use-mobile-agent-history-state.ts @@ -1,7 +1,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useHostClient, useForceReconnect } from '../transport/client-context' import type { - AiVaultListResult, AiVaultScanIssue, AiVaultScope, AiVaultSession @@ -28,8 +27,6 @@ export type AgentHistoryScreenState = | { kind: 'error'; message: string } | { kind: 'ready'; sessions: AiVaultSession[]; issues: AiVaultScanIssue[] } -type StatusWithCapabilities = { capabilities?: string[] } - export type MobileAgentHistoryStateParams = { hostId: string worktreeId: string @@ -96,11 +93,10 @@ export function useMobileAgentHistoryState(params: MobileAgentHistoryStateParams if (!isCurrent()) { return } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. const status = interpretOrThrowRefusalMessage( () => agentHistoryHostStatusRead.interpret(statusReply), 'Unable to reach host' - ) as StatusWithCapabilities + ) setHostStatusResult(status) if (!status.capabilities?.includes(MOBILE_AI_VAULT_CAPABILITY)) { setScreenState({ kind: 'unsupported' }) @@ -127,12 +123,17 @@ export function useMobileAgentHistoryState(params: MobileAgentHistoryStateParams if (!isCurrent()) { return } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. const result = interpretOrThrowRefusalMessage( () => agentHistorySessionScan.interpret(reply), 'Unable to load agent sessions' - ) as AiVaultListResult - setScreenState({ kind: 'ready', sessions: result.sessions, issues: result.issues }) + ) + setScreenState({ + kind: 'ready', + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the reader checks both containers; the rows stay the host's own records because `agent` is a vocabulary this client echoes back on resume. aivault-history-screen-listed `normal` records a full row: every member the cards and the resume path read unguarded. + sessions: result.sessions as AiVaultSession[], + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: same reader, same container check; the issue rows are the host's AiVaultScanIssue and are only counted, never read member-wise (MobileAgentSessionHistoryPanel.tsx:335). + issues: result.issues as AiVaultScanIssue[] + }) } catch (err) { if (!isCurrent()) { return diff --git a/mobile/src/components/MobileDictationSetupSheet.tsx b/mobile/src/components/MobileDictationSetupSheet.tsx index f4a81c02b8f..f590b10082f 100644 --- a/mobile/src/components/MobileDictationSetupSheet.tsx +++ b/mobile/src/components/MobileDictationSetupSheet.tsx @@ -25,7 +25,7 @@ type Props = { onReady?: () => void } -function formatSize(bytes: number | null): string { +function formatSize(bytes: number | null | undefined): string { if (!bytes) { return '' } diff --git a/mobile/src/components/MobileRepoIcon.tsx b/mobile/src/components/MobileRepoIcon.tsx index 2ea1f8d573b..b1c352382ee 100644 --- a/mobile/src/components/MobileRepoIcon.tsx +++ b/mobile/src/components/MobileRepoIcon.tsx @@ -23,7 +23,7 @@ import { Wrench } from 'lucide-react-native' import { Image, StyleSheet, Text, View } from 'react-native' -import type { RepoIcon } from '../../../src/shared/repo-icon' +import type { MobileRenderableRepoIcon } from '../host-screen/host-screen-reply-schema' import { colors } from '../theme/mobile-theme' // The lucide names the desktop repo-icon picker offers (src/renderer/src/ @@ -53,7 +53,9 @@ const REPO_LUCIDE_ICONS: Record = { } type Props = { - repoIcon?: RepoIcon | null + // Why the decoded catalog icon rather than the host's `RepoIcon`: this component reads + // `type`/`src`/`label`/`emoji`/`name` and never `source`, and a `RepoIcon` still satisfies it. + repoIcon?: MobileRenderableRepoIcon | null size?: number color?: string } diff --git a/mobile/src/components/VoiceModelList.tsx b/mobile/src/components/VoiceModelList.tsx index 4151f2591ad..a776945a075 100644 --- a/mobile/src/components/VoiceModelList.tsx +++ b/mobile/src/components/VoiceModelList.tsx @@ -17,7 +17,7 @@ type Props = { onDelete: (model: MobileSpeechModel) => void } -function formatSize(bytes: number | null): string { +function formatSize(bytes: number | null | undefined): string { if (!bytes) { return '' } diff --git a/mobile/src/components/WorktreeListRow.tsx b/mobile/src/components/WorktreeListRow.tsx index 1de862e630c..98684eab449 100644 --- a/mobile/src/components/WorktreeListRow.tsx +++ b/mobile/src/components/WorktreeListRow.tsx @@ -10,9 +10,9 @@ import { } from 'lucide-react-native' import { Pressable, StyleSheet, Text, View } from 'react-native' import { parseExecutionHostId, type ExecutionHostId } from '../../../src/shared/execution-host' -import type { RepoIcon } from '../../../src/shared/repo-icon' import type { AgentWorkingMode } from '../../../src/shared/agent-status-types' import type { RuntimeWorktreeAgentRow } from '../../../src/shared/runtime-types' +import type { MobileRenderableRepoIcon } from '../host-screen/host-screen-reply-schema' import { triggerMediumImpact } from '../platform/haptics' import { colors, radii, spacing, typography } from '../theme/mobile-theme' import { AgentSpinner } from './AgentSpinner' @@ -64,7 +64,7 @@ type Props = { isReadOnly: boolean now: number repoColor: string - repoIcon?: RepoIcon | null + repoIcon?: MobileRenderableRepoIcon | null // When the list is already grouped under this repo's section header, the row // omits its own repo icon+name to avoid the redundant "📁 orca" on every row. hideRepo?: boolean diff --git a/mobile/src/dictation/dictation-reply-schema.test.ts b/mobile/src/dictation/dictation-reply-schema.test.ts new file mode 100644 index 00000000000..aa241d885a6 --- /dev/null +++ b/mobile/src/dictation/dictation-reply-schema.test.ts @@ -0,0 +1,103 @@ +import { describe, expect, it } from 'vitest' +import { + dictationSetupSchema, + SPEECH_MODEL_PROVIDERS, + SPEECH_MODEL_STATUSES +} from './dictation-reply-schema' + +const setup = (overrides: Record = {}) => ({ + enabled: true, + selectedModelId: 'm1', + dictationMode: 'toggle', + models: [], + ...overrides +}) + +describe('dictation setup reply schema', () => { + it('requires the model list the sheet maps unguarded', () => { + const { models: _models, ...withoutModels } = setup() + expect(dictationSetupSchema.safeParse(withoutModels).success).toBe(false) + expect(dictationSetupSchema.safeParse(setup({ models: 'none' })).success).toBe(false) + }) + + it('forwards a dictation mode this build has never heard of instead of substituting one', () => { + expect(dictationSetupSchema.parse(setup({ dictationMode: 'push-to-talk' })).dictationMode).toBe( + 'push-to-talk' + ) + expect(dictationSetupSchema.parse(setup({ dictationMode: 'hold' })).dictationMode).toBe('hold') + }) + + it('keeps a sheet whose mode is absent or unreadable, which main rendered', () => { + const { dictationMode: _mode, ...withoutMode } = setup() + expect(dictationSetupSchema.parse(withoutMode).dictationMode).toBeUndefined() + expect(dictationSetupSchema.parse(setup({ dictationMode: 3 })).dictationMode).toBeUndefined() + }) + + it('salvages enabled and selectedModelId onto the off-and-unselected sheet main drew', () => { + const parsed = dictationSetupSchema.parse(setup({ enabled: 'yes', selectedModelId: 4 })) + expect(parsed.enabled).toBeUndefined() + expect(parsed.selectedModelId).toBeUndefined() + }) + + it('drops a model row the sheet could not address or send back', () => { + const parsed = dictationSetupSchema.parse( + setup({ models: [{ id: 'a', label: 'A' }, { label: 'B' }, { id: 'c' }] }) + ) + expect(parsed.models.map((model) => model.id)).toEqual(['a', 'c']) + }) + + it('degrades an unknown provider or status arm to absent and keeps the row', () => { + const [model] = dictationSetupSchema.parse( + setup({ models: [{ id: 'a', provider: 'anthropic', status: 'verifying' }] }) + ).models + expect(model?.id).toBe('a') + expect(model?.provider).toBeUndefined() + expect(model?.status).toBeUndefined() + }) + + it('keeps the known provider and status arms', () => { + const [model] = dictationSetupSchema.parse( + setup({ models: [{ id: 'a', provider: 'openai', status: 'extracting' }] }) + ).models + expect(model?.provider).toBe('openai') + expect(model?.status).toBe('extracting') + }) + + it('salvages a row member behind main own guard and keeps the truthy recommended flag', () => { + const [model] = dictationSetupSchema.parse( + setup({ models: [{ id: 'a', sizeBytes: 'big', progress: 'half', recommended: 1 }] }) + ).models + expect(model?.sizeBytes).toBeUndefined() + expect(model?.progress).toBeUndefined() + expect(model?.recommended).toBe(1) + }) + + it('keeps an explicit null size and progress, which the host sends for an API model', () => { + const [model] = dictationSetupSchema.parse( + setup({ models: [{ id: 'a', sizeBytes: null, progress: null }] }) + ).models + expect(model?.sizeBytes).toBeNull() + expect(model?.progress).toBeNull() + }) + + it('passes a newer host member through', () => { + expect(dictationSetupSchema.parse(setup({ hotword: 'orca' }))).toMatchObject({ + hotword: 'orca' + }) + }) +}) + +describe('provider and status are closed over the host union', () => { + // The arm lists are pinned to RuntimeSpeechModelSummary in the schema module, where tsc looks; + // this loop proves every pinned arm survives the parse, not just the two the tests above pick. + it('keeps every arm the host declares, so nothing it sends today degrades', () => { + for (const provider of SPEECH_MODEL_PROVIDERS) { + for (const status of SPEECH_MODEL_STATUSES) { + const [model] = dictationSetupSchema.parse( + setup({ models: [{ id: 'a', provider, status }] }) + ).models + expect(model).toMatchObject({ provider, status }) + } + } + }) +}) diff --git a/mobile/src/dictation/dictation-reply-schema.ts b/mobile/src/dictation/dictation-reply-schema.ts new file mode 100644 index 00000000000..bf35f3fcf7b --- /dev/null +++ b/mobile/src/dictation/dictation-reply-schema.ts @@ -0,0 +1,93 @@ +import { z } from 'zod' +import type { RuntimeSpeechModelSummary } from '../../../src/shared/runtime-worktree-contracts' +import { hostUnionArms, salvagedOptional, salvagingArray } from '../../../src/shared/zod-salvage' + +// The dictation setup sheet's reads and writes, and the three sends one dictation session makes. +// Checked against src/main/runtime/rpc/methods/speech.ts:12-59 and the shared results the speech +// catalog and dictation controller return: RuntimeSpeechSetupState and RuntimeSpeechModelSummary in +// src/shared/runtime-worktree-contracts.ts:81-96. + +// Pinned to the host's own union through hostUnionArms: an arm added or dropped host-side fails tsc. +export const SPEECH_MODEL_PROVIDERS = hostUnionArms({ + local: true, + openai: true +}) +export const SPEECH_MODEL_STATUSES = hostUnionArms({ + ready: true, + 'not-downloaded': true, + downloading: true, + extracting: true, + error: true +}) + +/** + * One model row in the setup sheet. + * + * `id` is required: it is the React key the list renders by, the value `isSelected` compares + * against, and — the reason it cannot be salvaged — the `modelId` the download, delete and select + * sends put back on the wire. A row without one drops rather than failing the whole sheet, which is + * the same rule the terminal inventory uses for a row it cannot address. + * + * `provider` and `status` are closed arm sets on the wire, so an arm this build has not heard of + * degrades to absent rather than refusing the reply or dropping the row. Absent is the right + * degrade here and not a coincidence: every read of either is an equality test against a known arm + * (`provider === 'openai'`, `status === 'ready'`, isModelInFlight), so an unknown arm already fell + * through to the same branch on main. Nothing is withheld that the row otherwise granted. + * + * `label`, `sizeBytes` and `progress` are decoration behind main's own guards — `!bytes`, + * `progress != null` — so a salvaged member lands on exactly the meta string main drew. + * `recommended` stays `z.unknown()` because the badge gates on truthiness, not on `=== true`. + */ +const speechModelSchema = z.looseObject({ + id: z.string(), + label: salvagedOptional('label', z.string()), + provider: salvagedOptional('provider', z.enum(SPEECH_MODEL_PROVIDERS)), + status: salvagedOptional('status', z.enum(SPEECH_MODEL_STATUSES)), + sizeBytes: salvagedOptional('sizeBytes', z.number().nullable()), + progress: salvagedOptional('progress', z.number().nullable()), + recommended: z.unknown().optional() +}) + +/** + * The whole dictation setup, which all three of the list, the delete and the config write answer + * with — the sheet renders the reply in place of a refetch, so one schema covers the three. + * + * `models` is required: MobileDictationSetupSheet.tsx:49 and VoiceModelList.tsx:53 read `.some` and + * `.map` on it with no guard, so a reply without one was a TypeError inside the sheet's own refresh. + * + * `dictationMode` is forwarded as the string the host sent, not as an arm set. Every consumer is an + * equality test against `toggle` or `hold` — the voice-settings segments, the terminal input mic and + * the native-chat composer — so a mode this build has not heard of renders the same inert control + * whether it arrives verbatim or as a substitute, and forwarding is the one that makes no version + * claim. It is NOT required even though the host declares it so: main rendered a sheet without one, + * and an absent mode stays absent here so the mic it feeds is as inert as main's was. + * + * `enabled` and `selectedModelId` stay salvaged: both are read behind `!`/`===` and a reply missing + * either renders an off switch and no selected row, which is what main rendered for the same reply. + */ +export const dictationSetupSchema = z.looseObject({ + enabled: salvagedOptional('enabled', z.boolean()), + selectedModelId: salvagedOptional('selectedModelId', z.string()), + dictationMode: salvagedOptional('dictationMode', z.string()), + models: salvagingArray(speechModelSchema) +}) + +export type MobileSpeechSetupReply = z.output +export type MobileSpeechModelReply = MobileSpeechSetupReply['models'][number] + +/** + * The five dictation sends whose reply body no call site reads. + * + * `speech.models.download` answers `{ started: true }` and the sheet polls the list instead; the + * start, chunk and cancel replies are interpreted for their acceptance verdict alone. Declaring a + * member on any of them would be a requirement with no reader behind it. + * + * `speech.dictation.finish` is here for a different reason, and it is the one site in this domain + * left deliberately unchecked. Its transcript is read at the call site through `rpcPayloadMember` + * (use-mobile-dictation.ts:237) *after* a staleness guard (:225), and the interpretation that a + * schema would fail runs before that guard. Checking it would report an unreadable reply for a + * dictation + * the user had already superseded, where main returned silently; the member read itself is guarded + * by `typeof transcript === 'string'` and is fenced by the raw-port inventory. + */ +export const dictationUnreadReplySchema = z.unknown() diff --git a/mobile/src/dictation/mobile-dictation-operations.ts b/mobile/src/dictation/mobile-dictation-operations.ts index b3471808c74..7cf1d96666b 100644 --- a/mobile/src/dictation/mobile-dictation-operations.ts +++ b/mobile/src/dictation/mobile-dictation-operations.ts @@ -1,9 +1,16 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc-operation' -import { rpcUncheckedPayloadReader } from '../transport/rpc-reader-payload' +import { rpcResultVariant } from '../transport/rpc-operation-result-reader' +import { dictationSetupSchema, dictationUnreadReplySchema } from './dictation-reply-schema' // The dictation setup sheet's reads and writes, and the three sends one dictation session makes. // Every refusing site here surfaces the host's own message with a screen fallback, so they share // one policy and differ only in the copy they fall back to, which stays at the call site. +// +// Three of the eight read a setup the sheet renders, and those are checked. The other five read no +// reply body at all, or read it past a guard whose order is load-bearing; dictation-reply-schema.ts +// says which and why. An unreadable setup now reaches the sheet's own catch through +// `interpretOrThrowRefusalMessage`, which shows the host-reply message where main showed +// `undefined` models and then crashed the refresh on `.some`. export const dictationSetupRead = bindDeferredRpcOperation( defineRpcOperation({ @@ -11,7 +18,7 @@ export const dictationSetupRead = bindDeferredRpcOperation( method: 'speech.models.list', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('dictation-setup') + read: rpcResultVariant('dictation-setup', dictationSetupSchema) }) ) @@ -22,7 +29,7 @@ export const dictationModelDownload = bindDeferredRpcOperation( method: 'speech.models.download', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('dictation-download-started') + read: rpcResultVariant('dictation-download-started', dictationUnreadReplySchema) }) ) @@ -33,7 +40,7 @@ export const dictationModelDelete = bindDeferredRpcOperation( method: 'speech.models.delete', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('dictation-setup') + read: rpcResultVariant('dictation-setup', dictationSetupSchema) }) ) @@ -43,7 +50,7 @@ export const dictationConfigWrite = bindDeferredRpcOperation( method: 'speech.dictation.setup', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('dictation-setup') + read: rpcResultVariant('dictation-setup', dictationSetupSchema) }) ) @@ -53,7 +60,7 @@ export const dictationSessionStart = bindDeferredRpcOperation( method: 'speech.dictation.start', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('dictation-started') + read: rpcResultVariant('dictation-started', dictationUnreadReplySchema) }) ) @@ -63,7 +70,7 @@ export const dictationAudioChunkSend = bindDeferredRpcOperation( method: 'speech.dictation.chunk', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('dictation-chunk-received') + read: rpcResultVariant('dictation-chunk-received', dictationUnreadReplySchema) }) ) @@ -78,7 +85,7 @@ export const dictationSessionFinish = bindDeferredRpcOperation( method: 'speech.dictation.finish', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('dictation-finished') + read: rpcResultVariant('dictation-finished', dictationUnreadReplySchema) }) ) @@ -94,6 +101,6 @@ export const dictationSessionCancel = bindDeferredRpcOperation( method: 'speech.dictation.cancel', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('dictation-cancelled') + read: rpcResultVariant('dictation-cancelled', dictationUnreadReplySchema) }) ) diff --git a/mobile/src/dictation/mobile-dictation-setup.test.ts b/mobile/src/dictation/mobile-dictation-setup.test.ts index f84e545af49..a732593f14f 100644 --- a/mobile/src/dictation/mobile-dictation-setup.test.ts +++ b/mobile/src/dictation/mobile-dictation-setup.test.ts @@ -62,14 +62,24 @@ describe('isDictationSetupRequiredError', () => { describe('rpc wrappers', () => { it('fetches setup', async () => { - const setup: MobileSpeechSetup = { enabled: false, selectedModelId: '', models: [] } + const setup: MobileSpeechSetup = { + enabled: false, + selectedModelId: '', + dictationMode: 'toggle', + models: [] + } const client = clientWith([ok(setup)]) await expect(fetchDictationSetup(client)).resolves.toEqual(setup) expect(client.calls[0]).toEqual({ method: 'speech.models.list', params: null }) }) it('retries the idempotent setup read once after logical-client cutover', async () => { - const setup: MobileSpeechSetup = { enabled: false, selectedModelId: '', models: [] } + const setup: MobileSpeechSetup = { + enabled: false, + selectedModelId: '', + dictationMode: 'toggle', + models: [] + } const sendRequest = vi .fn() .mockRejectedValueOnce(new LogicalClientCutoverError()) @@ -95,14 +105,24 @@ describe('rpc wrappers', () => { }) it('deletes a model and returns refreshed setup', async () => { - const setup: MobileSpeechSetup = { enabled: true, selectedModelId: '', models: [] } + const setup: MobileSpeechSetup = { + enabled: true, + selectedModelId: '', + dictationMode: 'toggle', + models: [] + } const client = clientWith([ok(setup)]) await expect(deleteDictationModel(client, 'm1')).resolves.toEqual(setup) expect(client.calls[0]).toEqual({ method: 'speech.models.delete', params: { modelId: 'm1' } }) }) it('sets config', async () => { - const setup: MobileSpeechSetup = { enabled: true, selectedModelId: 'm1', models: [] } + const setup: MobileSpeechSetup = { + enabled: true, + selectedModelId: 'm1', + dictationMode: 'hold', + models: [] + } const client = clientWith([ok(setup)]) await expect(setDictationConfig(client, { enabled: true, modelId: 'm1' })).resolves.toEqual( setup @@ -181,6 +201,7 @@ describe('state helpers', () => { isDictationReady({ enabled: true, selectedModelId: 'm1', + dictationMode: 'toggle', models: [model({ status: 'ready' })] }) ).toBe(true) @@ -188,6 +209,7 @@ describe('state helpers', () => { isDictationReady({ enabled: false, selectedModelId: 'm1', + dictationMode: 'toggle', models: [model({ status: 'ready' })] }) ).toBe(false) @@ -195,9 +217,17 @@ describe('state helpers', () => { isDictationReady({ enabled: true, selectedModelId: 'm1', + dictationMode: 'toggle', models: [model({ status: 'not-downloaded' })] }) ).toBe(false) - expect(isDictationReady({ enabled: true, selectedModelId: '', models: [] })).toBe(false) + expect( + isDictationReady({ + enabled: true, + selectedModelId: '', + dictationMode: 'toggle', + models: [] + }) + ).toBe(false) }) }) diff --git a/mobile/src/dictation/mobile-dictation-setup.ts b/mobile/src/dictation/mobile-dictation-setup.ts index 9ac8aae5d21..6f3002bea8c 100644 --- a/mobile/src/dictation/mobile-dictation-setup.ts +++ b/mobile/src/dictation/mobile-dictation-setup.ts @@ -1,4 +1,3 @@ -import type { RuntimeSpeechSetupState } from '../../../src/shared/runtime-types' import type { RpcClient } from '../transport/rpc-client' import type { RpcResponse } from '../transport/types' import { interpretOrThrowRefusalMessage } from '../transport/rpc-refusal-message' @@ -9,9 +8,12 @@ import { dictationModelDownload, dictationSetupRead } from './mobile-dictation-operations' +import type { MobileSpeechModelReply, MobileSpeechSetupReply } from './dictation-reply-schema' -export type MobileSpeechSetup = RuntimeSpeechSetupState -export type MobileSpeechModel = RuntimeSpeechSetupState['models'][number] +// The setup as the reply reader hands it back, not RuntimeSpeechSetupState: the reader salvages the +// members the sheet reads behind a guard, so the screens see what it actually checked. +export type MobileSpeechSetup = MobileSpeechSetupReply +export type MobileSpeechModel = MobileSpeechModelReply // Dictation-setup errors startMobileDictation throws when the desktop isn't // configured. Mapping them lets the mic entry point open the setup sheet @@ -45,11 +47,10 @@ export async function fetchDictationSetup(client: RpcClient): Promise dictationSetupRead.interpret(reply), 'Failed to load dictation models' - ) as MobileSpeechSetup + ) } async function requestDictationSetupReply(client: RpcClient): Promise { @@ -78,11 +79,10 @@ export async function deleteDictationModel( modelId: string ): Promise { const reply = await dictationModelDelete.request(client, { modelId }) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. return interpretOrThrowRefusalMessage( () => dictationModelDelete.interpret(reply), 'Failed to delete model' - ) as MobileSpeechSetup + ) } export async function setDictationConfig( @@ -90,11 +90,10 @@ export async function setDictationConfig( params: { enabled?: boolean; modelId?: string; dictationMode?: 'toggle' | 'hold' } ): Promise { const reply = await dictationConfigWrite.request(client, params) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. return interpretOrThrowRefusalMessage( () => dictationConfigWrite.interpret(reply), 'Failed to update dictation settings' - ) as MobileSpeechSetup + ) } // A model is mid-download (or extracting) and the sheet should keep polling. diff --git a/mobile/src/files/MobileFileExplorerPanel.tsx b/mobile/src/files/MobileFileExplorerPanel.tsx index 619bfd5c4a2..84b05e0fb43 100644 --- a/mobile/src/files/MobileFileExplorerPanel.tsx +++ b/mobile/src/files/MobileFileExplorerPanel.tsx @@ -16,8 +16,7 @@ import { flattenDirectoryCache, getDirectoryCacheState, type DirectoryCache, - type FileExplorerRow, - type MobileDirEntry + type FileExplorerRow } from './file-tree' import type { RpcFailure } from '../transport/types' import { colors } from '../theme/mobile-theme' @@ -28,11 +27,7 @@ import { resetDirectoryLoadRevisions, type DirectoryLoadRevisions } from './directory-load-revisions' -import { - directoryCacheFromFileList, - isMobileMethodUnavailableError, - type LegacyFilesListResult -} from './file-list-fallback' +import { directoryCacheFromFileList, isMobileMethodUnavailableError } from './file-list-fallback' import { fileDirectoryRead, legacyFileListRead } from './mobile-file-explorer-operations' import { fileExplorerStyles as styles } from './mobile-file-explorer-styles' import { MobileFileExplorerRow } from './mobile-file-explorer-row' @@ -134,8 +129,7 @@ export function MobileFileExplorerPanel(props: { ) { return } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. - const legacyResult = legacy.value as LegacyFilesListResult + const legacyResult = legacy.value setDirectoryCache(directoryCacheFromFileList(legacyResult.files)) // Why: the capped list silently omits files past the cap — keep // the legacy explorer's "Showing first 5000" note. @@ -156,8 +150,7 @@ export function MobileFileExplorerPanel(props: { ) { return } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. - const entries = directory.value as MobileDirEntry[] + const entries = directory.value if (rootLoad) { setLegacyListTruncated(false) } diff --git a/mobile/src/files/file-explorer-reply-schema.test.ts b/mobile/src/files/file-explorer-reply-schema.test.ts new file mode 100644 index 00000000000..362d2d7f657 --- /dev/null +++ b/mobile/src/files/file-explorer-reply-schema.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from 'vitest' +import { fileDirectoryEntriesSchema, legacyFileListSchema } from './file-explorer-reply-schema' + +describe('file explorer reply schemas', () => { + it('requires the listing itself to be an array', () => { + expect(fileDirectoryEntriesSchema.safeParse({ entries: [] }).success).toBe(false) + expect(fileDirectoryEntriesSchema.safeParse([]).success).toBe(true) + }) + + it('drops a row the tree cannot place and keeps the rest of the directory', () => { + const parsed = fileDirectoryEntriesSchema.parse([ + { name: 'src', isDirectory: true }, + { isDirectory: false }, + { name: 'readme.md' }, + { name: 'main.ts', isDirectory: false, isSymlink: true } + ]) + expect(parsed.map((entry) => entry.name)).toEqual(['src', 'main.ts']) + }) + + it('salvages isSymlink without dropping the row', () => { + expect( + fileDirectoryEntriesSchema.parse([{ name: 'a', isDirectory: false, isSymlink: 'yes' }])[0] + ?.isSymlink + ).toBeUndefined() + }) + + it('requires the capped list and the note it draws', () => { + const listed = { files: [], truncated: true } + expect(legacyFileListSchema.safeParse(listed).success).toBe(true) + // One at a time, so a sibling requirement cannot stand in for the member under test. + for (const member of ['files', 'truncated'] as const) { + const { [member]: _dropped, ...without } = listed + expect(legacyFileListSchema.safeParse(without).success).toBe(false) + } + }) + + it('drops a legacy row that names no path and keeps the rest', () => { + const parsed = legacyFileListSchema.parse({ + files: [{ relativePath: 'a/b.ts' }, { basename: 'c.ts' }, { relativePath: 4 }], + truncated: false + }) + expect(parsed.files).toEqual([{ relativePath: 'a/b.ts' }]) + }) + + it('passes a newer host member through on a directory row', () => { + expect( + fileDirectoryEntriesSchema.parse([{ name: 'a', isDirectory: false, sizeBytes: 12 }])[0] + ).toMatchObject({ sizeBytes: 12 }) + }) +}) diff --git a/mobile/src/files/file-explorer-reply-schema.ts b/mobile/src/files/file-explorer-reply-schema.ts new file mode 100644 index 00000000000..696bdde55a1 --- /dev/null +++ b/mobile/src/files/file-explorer-reply-schema.ts @@ -0,0 +1,45 @@ +import { z } from 'zod' +import { salvagedOptional, salvagingArray } from '../../../src/shared/zod-salvage' + +// The Files tab's directory read and the capped list it falls back to. Checked against +// src/main/runtime/rpc/methods/files.ts:125 and :27, and the shared results they return: +// DirEntry rows from readFileExplorerDir, RuntimeFileListResult from listMobileFiles. + +/** + * One directory's entries. + * + * The payload is the array itself, and it is required: MobileFileExplorerPanel.tsx:157 puts it + * straight into the directory cache, where flattenDirectoryCache (file-tree.ts:58) sorts and walks + * it — a reply that was not an array was a `.filter` on a string one render later, with nothing + * naming the reply. + * + * A row needs `name` and `isDirectory`, and a row without either drops rather than failing the + * whole read, which is what a skip policy means for a directory listing: compareFileNames reads + * `name` unguarded, and `isDirectory` is the discriminator the whole tree projection turns on, so a + * row without it is a file that can never be opened and whose label renders `undefined`. + * `isSymlink` is decoration on the row and stays salvaged. + */ +export const fileDirectoryEntriesSchema = salvagingArray( + z.looseObject({ + name: z.string(), + isDirectory: z.boolean(), + isSymlink: salvagedOptional('isSymlink', z.boolean()) + }) +) + +/** + * The capped flat list an older desktop answers when `files.readDir` is not allowlisted. + * + * `files` and `truncated` are both required and both read unguarded: directoryCacheFromFileList + * walks `files` and splits each `relativePath` (file-list-fallback.ts:48), and + * MobileFileExplorerPanel.tsx:136 publishes `truncated` into the state that draws the "Showing + * first 5000" note. A row without a string `relativePath` drops — it can name no directory — where + * main crashed the whole fallback on it. + * + * `basename` and `kind` are not declared: this consumer reads neither, and passthrough keeps them + * for the inventory reader that does. + */ +export const legacyFileListSchema = z.looseObject({ + files: salvagingArray(z.looseObject({ relativePath: z.string() })), + truncated: z.boolean() +}) diff --git a/mobile/src/files/file-list-fallback.ts b/mobile/src/files/file-list-fallback.ts index aa7323e13b5..e0db7236bad 100644 --- a/mobile/src/files/file-list-fallback.ts +++ b/mobile/src/files/file-list-fallback.ts @@ -29,7 +29,11 @@ export function isMobileMethodUnavailableError( ) } -export function directoryCacheFromFileList(files: LegacyMobileFileEntry[]): DirectoryCache { +// Takes only the member it reads: the explorer's reply reader checks `relativePath` and passes the +// rest of each row through, so naming the whole row here would re-declare what it deliberately did not. +export function directoryCacheFromFileList( + files: readonly { relativePath: string; [member: string]: unknown }[] +): DirectoryCache { const childrenByDir = new Map>() const ensureDir = (path: string): Map => { let children = childrenByDir.get(path) diff --git a/mobile/src/files/file-ownership-reply-schema.test.ts b/mobile/src/files/file-ownership-reply-schema.test.ts new file mode 100644 index 00000000000..33930873217 --- /dev/null +++ b/mobile/src/files/file-ownership-reply-schema.test.ts @@ -0,0 +1,51 @@ +import { describe, expect, it } from 'vitest' +import { + fileOwnershipSshStateSchema, + fileOwnershipWorktreeSchema +} from './file-ownership-reply-schema' + +describe('file ownership reply schemas', () => { + it('keeps the three hostId states distinct', () => { + // Absent means no host recorded and captures local; explicit null means the host said local; + // a string is parsed. Collapsing absent and null would change where a write lands. + expect(fileOwnershipWorktreeSchema.parse({ worktree: {} })?.hostId).toBeUndefined() + expect(fileOwnershipWorktreeSchema.parse({ worktree: { hostId: null } })?.hostId).toBeNull() + expect(fileOwnershipWorktreeSchema.parse({ worktree: { hostId: 'ssh:a' } })?.hostId).toBe( + 'ssh:a' + ) + }) + + it('refuses a wrong-typed hostId instead of salvaging it to local', () => { + expect(fileOwnershipWorktreeSchema.safeParse({ worktree: { hostId: 7 } }).success).toBe(false) + }) + + it('keeps an unresolved worktree readable as the absent summary main threw on', () => { + expect(fileOwnershipWorktreeSchema.parse({})).toBeUndefined() + expect(fileOwnershipWorktreeSchema.parse({ worktree: null })).toBeNull() + }) + + it('passes the connection generation through and refuses a wrong-typed one', () => { + // The generation is echoed back to the host on the mutation, so a client-side fallback here + // would put a value on the wire the host then refuses. + expect( + fileOwnershipSshStateSchema.parse({ state: { targetId: 't', connectionGeneration: 3 } }) + ?.connectionGeneration + ).toBe(3) + expect( + fileOwnershipSshStateSchema.safeParse({ state: { targetId: 't', connectionGeneration: '3' } }) + .success + ).toBe(false) + }) + + it('salvages a wrong-typed targetId onto the mismatch main threw', () => { + expect( + fileOwnershipSshStateSchema.parse({ state: { targetId: 7, connectionGeneration: 1 } }) + ?.targetId + ).toBeUndefined() + }) + + it('reads a host holding no connection as the null state it sends', () => { + expect(fileOwnershipSshStateSchema.parse({ state: null })).toBeNull() + expect(fileOwnershipSshStateSchema.parse({})).toBeUndefined() + }) +}) diff --git a/mobile/src/files/file-ownership-reply-schema.ts b/mobile/src/files/file-ownership-reply-schema.ts new file mode 100644 index 00000000000..aa6d3915bcf --- /dev/null +++ b/mobile/src/files/file-ownership-reply-schema.ts @@ -0,0 +1,53 @@ +import { z } from 'zod' +import { salvagedOptional } from '../../../src/shared/zod-salvage' + +// The two reads that pin which execution host owns a workspace before a file mutation is sent. +// Checked against src/main/runtime/rpc/methods/worktree.ts:48 and ssh.ts:30, and the shared +// SshConnectionState in src/shared/ssh-types.ts:187. +// +// This capture decides where a write lands, so the usual "degrade to absent" salvage is wrong for +// the two members it routes on: absent reads as *local* downstream, and turning an unreadable owner +// into a local one is how a mutation reaches the wrong host. Both are declared fatal instead. + +/** + * The workspace row a mutation targets. + * + * The payload wrapper stays nullish so an absent `worktree` still reaches + * mobile-file-mutation-ownership.ts:68 as the `!summary` throw main had, rather than as a decode + * failure — the host answers `{ worktree: undefined }` for a selector it cannot resolve. + * + * `hostId` is a plain nullable optional, not a salvaged one, and the distinction is load-bearing + * twice over. Its three states are distinct to buildMobileFileMutationOwnership: absent means "no + * host recorded" and yields a local capture, `null` means the reply named a host this client cannot + * place and is refused (mobile-file-mutation-ownership.ts:32, pinned at its test:122), and a string + * is parsed. A salvage would fold a *wrong-typed* hostId into absent and let the mutation go local; + * main threw "Couldn't verify the SSH connection" on it, and an incompatible reply throws too. + */ +export const fileOwnershipWorktreeSchema = z + .looseObject({ + worktree: z.looseObject({ hostId: z.string().nullable().optional() }).nullish() + }) + .transform((reply) => reply.worktree) + +/** + * The SSH connection generation the mutation is expected to still be running on. + * + * `connectionGeneration` is echoed back to the host as `expectedSshConnectionGeneration` on the + * mutation itself, so it is passed through at its own type and a wrong type is fatal: a reply-side + * fallback here would put a value on the wire that the host then refuses, and main's + * `=== undefined` check would have let a non-number through unnoticed. + * + * `targetId` is only ever compared against the parsed host's own target, so a salvaged member lands + * on exactly main's mismatch throw. The `state` member itself is nullish because the host answers + * `{ state: null }` for a target it holds no connection for, which is the normal local case. + */ +export const fileOwnershipSshStateSchema = z + .looseObject({ + state: z + .looseObject({ + targetId: salvagedOptional('targetId', z.string()), + connectionGeneration: z.number().optional() + }) + .nullish() + }) + .transform((reply) => reply.state) diff --git a/mobile/src/files/file-preview-reply-schema.test.ts b/mobile/src/files/file-preview-reply-schema.test.ts new file mode 100644 index 00000000000..4810c5688ad --- /dev/null +++ b/mobile/src/files/file-preview-reply-schema.test.ts @@ -0,0 +1,70 @@ +import { describe, expect, it } from 'vitest' +import { + filePreviewImageSchema, + filePreviewTextSchema, + terminalPathResolutionSchema +} from './file-preview-reply-schema' + +describe('file preview reply schemas', () => { + it('requires the content the markdown disk fallback publishes unguarded', () => { + // `content` alone, with no sibling requirement able to stand in for it. + expect(filePreviewTextSchema.safeParse({ truncated: false, byteLength: 0 }).success).toBe(false) + expect(filePreviewTextSchema.safeParse({ content: '# readme' }).success).toBe(true) + expect(filePreviewTextSchema.safeParse({ content: 7 }).success).toBe(false) + }) + + it('salvages truncated and byteLength onto the fallbacks main already had', () => { + const parsed = filePreviewTextSchema.parse({ + content: 'body', + truncated: 'yes', + byteLength: 'four' + }) + // Absent reads as not truncated, and an unreadable byteLength falls through to content.length + // at the call site, which is what main's `typeof === 'number'` guard already did. + expect(parsed.truncated).toBeUndefined() + expect(parsed.byteLength).toBeUndefined() + }) + + it('keeps every member of an image preview optional so the host binary arms still render', () => { + // The host answers this shape for a binary it cannot preview, and this one for a path mobile + // classified as an image and the host did not. Both reach the screen as main's own copy. + expect(filePreviewImageSchema.safeParse({ content: '', isBinary: true }).success).toBe(true) + expect(filePreviewImageSchema.safeParse({ content: 'text', isBinary: false }).success).toBe( + true + ) + }) + + it('salvages an image preview member to the arm main fell back to', () => { + const parsed = filePreviewImageSchema.parse({ + content: 'aGk=', + isImage: 'yes', + mimeType: 7 + }) + expect(parsed.isImage).toBeUndefined() + expect(parsed.mimeType).toBeUndefined() + }) + + it('refuses a preview payload that is not an object', () => { + expect(filePreviewTextSchema.safeParse('# readme').success).toBe(false) + expect(filePreviewImageSchema.safeParse(null).success).toBe(false) + }) + + it('keeps a terminal path resolution readable with every member salvaged', () => { + const parsed = terminalPathResolutionSchema.parse({ + exists: 'yes', + isDirectory: false, + openTarget: { kind: 'absolute-file', absolutePath: '/logs/run.txt', grantId: 'g2' } + }) + expect(parsed.exists).toBeUndefined() + expect(parsed.openTarget?.grantId).toBe('g2') + }) + + it('passes a newer host member through on every preview schema', () => { + expect(filePreviewTextSchema.parse({ content: 'a', encoding: 'utf8' })).toMatchObject({ + encoding: 'utf8' + }) + expect( + filePreviewImageSchema.parse({ content: 'a', imageDimensions: { width: 1 } }) + ).toMatchObject({ imageDimensions: { width: 1 } }) + }) +}) diff --git a/mobile/src/files/file-preview-reply-schema.ts b/mobile/src/files/file-preview-reply-schema.ts new file mode 100644 index 00000000000..8c35df8eb39 --- /dev/null +++ b/mobile/src/files/file-preview-reply-schema.ts @@ -0,0 +1,92 @@ +import { z } from 'zod' +import { salvagedOptional } from '../../../src/shared/zod-salvage' + +// What the preview screen reads for one file, and what the grant refresh reads to re-mint a stale +// grant. Checked against the four handlers in src/main/runtime/rpc/methods/files.ts:70/104 and +// files-terminal-artifact-methods.ts:10/21/42, and the shared results they return verbatim: +// RuntimeFileReadResult, RuntimeFilePreviewResult and RuntimeTerminalPathResolution in +// src/shared/runtime-file-contracts.ts. +// +// Three encodings are used here and mean three different things, so they are stated once: +// - a required member is one a consumer reads with no guard, where absence renders `undefined` +// or throws on the next property; +// - `salvagedOptional(name, T)` is for a member behind a *typed* guard with a fallback — the +// salvage lands on exactly that fallback, so the screen shows what main showed; +// - `z.unknown()` is for a member read only for truthiness, because narrowing it would move +// main's answer for a value the consumer's own guard already accepted. + +/** + * A file's text, for `files.read` and `files.readTerminalArtifact` alike: one host result type, and + * the preview screen normalizes both through the same projection. + * + * `content` is required because the markdown disk fallback reads it with no guard — + * use-mobile-session-document-readers.ts:60 publishes it straight into the tab's ready document, so + * a reply without one rendered `undefined` in the editor. The preview screen's own reader guards it + * (`typeof preview.content !== 'string'` in mobile-file-preview-response.ts:142) and lands on + * 'Unable to load preview', which is the same copy `previewError` gives the incompatible-reply + * message — so requiring it moves the preview screen's text not at all. + * + * `truncated` and `byteLength` stay salvaged. Both are declared required by RuntimeFileReadResult, + * but neither can crash or render garbage: `truncated` is a truthiness test behind a read-only + * reason string and `byteLength` has main's own `preview.content.length` fallback behind a + * `typeof === 'number'` guard. Requiring either would only let a host that trims a field take the + * whole preview down. `isBinary` is never sent on these two methods — the host raises `binary_file` + * instead — but the projection still checks it, so it is declared where main looked for it. + */ +export const filePreviewTextSchema = z.looseObject({ + content: z.string(), + truncated: salvagedOptional('truncated', z.boolean()), + byteLength: salvagedOptional('byteLength', z.number()), + isBinary: salvagedOptional('isBinary', z.boolean()) +}) + +/** + * A file's image bytes, for `files.readPreview` and `files.readTerminalArtifactPreview`. + * + * Nothing is required: normalizeImagePreviewResult guards all four members and falls back to + * `previewError('binary_file')` for every one of them, and that fallback is load-bearing — the host + * answers `{ content, isBinary: true }` with no mime for a binary it cannot preview, and + * `{ content, isBinary: false }` for a path mobile classifies as an image and the host does not. + * Both are good replies the screen renders today. The guards are all `=== true` / `!== true` / + * `typeof === 'string'`, so a salvaged member lands on exactly the arm main took. + * + * What the schema adds is the container. A bare string or a null result reached the projection as + * 'Binary preview unavailable', which names the file rather than the reply. + */ +export const filePreviewImageSchema = z.looseObject({ + content: salvagedOptional('content', z.string()), + isBinary: salvagedOptional('isBinary', z.boolean()), + isImage: salvagedOptional('isImage', z.boolean()), + mimeType: salvagedOptional('mimeType', z.string()) +}) + +/** + * A terminal path re-resolved to mint a fresh grant. + * + * Nothing is required and every member is salvaged: isTerminalArtifactResolution + * (mobile-terminal-artifact-grant-refresh.ts:77) is a total guard that answers "not refreshable" + * for anything it cannot read, and a refusal to refresh is a normal outcome rather than an error. + * The schema declares the members that guard reads so a newer host's extra keys pass through, and + * adds only the container. + */ +export const terminalPathResolutionSchema = z.looseObject({ + exists: salvagedOptional('exists', z.boolean()), + isDirectory: salvagedOptional('isDirectory', z.boolean()), + openTarget: salvagedOptional( + 'openTarget', + z.looseObject({ + kind: salvagedOptional('kind', z.string()), + absolutePath: salvagedOptional('absolutePath', z.string()), + grantId: salvagedOptional('grantId', z.string()), + readOnly: salvagedOptional('readOnly', z.literal(true)) + }) + ) +}) + +/** + * The artifact save's reply body, which no call site reads. + * + * `writeTerminalArtifactFile` answers `{ ok: true }` and settlePreviewSend looks only at the + * acceptance verdict, so declaring a member would be a requirement with no reader behind it. + */ +export const terminalArtifactWriteSchema = z.unknown() diff --git a/mobile/src/files/file-tab-doc-reply-schema.test.ts b/mobile/src/files/file-tab-doc-reply-schema.test.ts new file mode 100644 index 00000000000..f561416b262 --- /dev/null +++ b/mobile/src/files/file-tab-doc-reply-schema.test.ts @@ -0,0 +1,76 @@ +import { describe, expect, it } from 'vitest' +import { + fileTabBinaryDiffSchema, + fileTabImageSchema, + fileTabTextDiffSchema, + fileTabTextSchema +} from './file-tab-doc-reply-schema' + +describe('file tab doc reply schemas', () => { + it('requires the three members the tab publishes into a ready document', () => { + const ready = { content: 'a', truncated: false, byteLength: 1 } + expect(fileTabTextSchema.safeParse(ready).success).toBe(true) + // One at a time, so a sibling requirement cannot stand in for the member under test. + for (const member of ['content', 'truncated', 'byteLength'] as const) { + const { [member]: _dropped, ...without } = ready + expect(fileTabTextSchema.safeParse(without).success).toBe(false) + } + }) + + it('requires the image content buildImageDataUri calls replace on', () => { + expect(fileTabImageSchema.safeParse({ isImage: true, mimeType: 'image/png' }).success).toBe( + false + ) + }) + + it('keeps an image tab readable for the truthy non-boolean isImage main rendered', () => { + expect(fileTabImageSchema.parse({ content: 'aGk=', isImage: 1 }).isImage).toBe(1) + }) + + it('refuses a wrong-typed mimeType rather than salvaging it', () => { + // Main threw on `mimeType?.startsWith`, and readFileTab's catch showed + // "Couldn't load file preview"; salvaging to absent would have shown the binary copy instead. + expect(fileTabImageSchema.safeParse({ content: 'aGk=', mimeType: 7 }).success).toBe(false) + }) + + it('reads a text diff only when both sides are strings', () => { + expect( + fileTabTextDiffSchema.safeParse({ kind: 'text', originalContent: 'a', modifiedContent: 'b' }) + .success + ).toBe(true) + expect(fileTabTextDiffSchema.safeParse({ kind: 'text', originalContent: 'a' }).success).toBe( + false + ) + }) + + it('routes an unknown diff arm to the binary reader and never the text one', () => { + expect(fileTabBinaryDiffSchema.parse({ kind: 'submodule' }).kind).toBe('binary') + expect(fileTabBinaryDiffSchema.parse({ kind: 'binary' }).kind).toBe('binary') + // A text diff whose contents did not arrive must not be re-read as binary: the tab would name + // the file unpreviewable instead of naming the reply. + expect(fileTabBinaryDiffSchema.safeParse({ kind: 'text' }).success).toBe(false) + }) + + it('salvages the binary arm members main compared against true', () => { + const parsed = fileTabBinaryDiffSchema.parse({ + kind: 'binary', + isImage: 'yes', + modifiedDeleted: 1, + modifiedContent: 5 + }) + expect(parsed.isImage).toBeUndefined() + expect(parsed.modifiedDeleted).toBeUndefined() + expect(parsed.modifiedContent).toBeUndefined() + }) + + it('passes a newer host member through', () => { + expect( + fileTabTextDiffSchema.parse({ + kind: 'text', + originalContent: 'a', + modifiedContent: 'b', + largeDiffRenderLimit: { lines: 10 } + }) + ).toMatchObject({ largeDiffRenderLimit: { lines: 10 } }) + }) +}) diff --git a/mobile/src/files/file-tab-doc-reply-schema.ts b/mobile/src/files/file-tab-doc-reply-schema.ts new file mode 100644 index 00000000000..e25f613b456 --- /dev/null +++ b/mobile/src/files/file-tab-doc-reply-schema.ts @@ -0,0 +1,96 @@ +import { z } from 'zod' +import { salvagedOptional } from '../../../src/shared/zod-salvage' + +// What a session file tab reads to render one document. Checked against +// src/main/runtime/rpc/methods/files.ts:70/104 and git-diff-methods.ts:16, and the shared results +// they return: RuntimeFileReadResult and RuntimeFilePreviewResult in runtime-file-contracts.ts, +// GitDiffTextResult / GitDiffBinaryResult in git-diff-compare-types.ts. +// +// A tab is stricter than the preview screen on the same two file methods, and that is a property of +// the consumer rather than of the host: resolveMobileFileTabDoc publishes what it reads straight +// into a typed ready document with no guard, where the preview screen normalizes every member. + +/** + * The text a file tab renders, from `files.read`. + * + * All three are required because all three are published unguarded into MobileFileTabDoc: + * mobile-file-tab-doc.ts:68 renders `content` as the html body and :73-75 puts `content`, + * `truncated` and `byteLength` into the `file` arm, whose size label and truncation banner read + * them as a number and a boolean. A reply missing one rendered `undefined` in the tab. All three + * are declared required by RuntimeFileReadResult, so no host that answers this method omits them. + */ +export const fileTabTextSchema = z.looseObject({ + content: z.string(), + truncated: z.boolean(), + byteLength: z.number() +}) + +/** + * The image bytes a file tab renders, from `files.readPreview`. + * + * `content` is required: buildImageDataUri runs `base64Content.replace` with no guard once + * `isImage` is truthy (mobile-file-tab-doc.ts:58), so a reply without a string content was a + * TypeError. RuntimeFilePreviewResult declares it required. + * + * `mimeType` is a plain optional rather than a salvaged one, because main had no fallback for a + * wrong type here either: `mimeType?.startsWith` threw on a non-string, and readFileTab's catch + * showed "Couldn't load file preview". An incompatible reply reaches that same catch with that same + * copy, where salvaging to absent would instead have shown 'Binary preview unavailable'. + * + * `isImage` stays `z.unknown()`: the tab gates on its truthiness, not on `=== true`, so narrowing + * it to a boolean would drop an image main rendered. + */ +export const fileTabImageSchema = z.looseObject({ + content: z.string(), + isImage: z.unknown().optional(), + mimeType: z.string().optional() +}) + +/** + * The text arm of `git.diff`: the only arm whose contents are read. + * + * Both sides are required because buildMobileDiffLines reads `content.length` on each with no + * guard (mobile-diff-lines.ts:35), so a text diff missing one was a TypeError caught as + * "Couldn't load diff preview". + */ +const fileTabTextDiffSchema = z.looseObject({ + kind: z.literal('text'), + originalContent: z.string(), + modifiedContent: z.string() +}) + +/** + * Every other arm of `git.diff`, including one this build has not heard of. + * + * The arm set is a wire surface, so an unknown `kind` degrades here rather than refusing the reply: + * mobile-file-tab-doc.ts:41 asks only `kind !== 'text'`, and an unknown arm took this branch on + * main too. `kind` is therefore any string but `text` — routing an unreadable *text* diff here + * instead would render "Binary preview unavailable" for a diff whose contents simply did not + * arrive, which names the file rather than the reply. + * + * Nothing in the arm is required: mobileDiffImageDataUri guards every member it reads + * (mobile-diff-image-preview.ts:22-33) and answers null — 'binary_file' — for anything it cannot + * use. `isImage` and `modifiedDeleted` are `=== true` comparisons, so a salvaged member lands on + * main's own arm; `mimeType` is a plain optional for the same reason the image tab's is. + * + * `kind` is admitted as any string but `text` and answered as `binary`, because that is the arm the + * client resolved rather than the token the host sent: no consumer forwards or renders it, and + * naming it `binary` is what lets the tab tell the two arms apart without re-testing the string. + */ +const fileTabBinaryDiffSchema = z.looseObject({ + kind: z + .string() + .refine((kind) => kind !== 'text', 'not the text arm') + .transform(() => 'binary' as const), + originalContent: salvagedOptional('originalContent', z.string()), + modifiedContent: salvagedOptional('modifiedContent', z.string()), + isImage: salvagedOptional('isImage', z.boolean()), + modifiedDeleted: salvagedOptional('modifiedDeleted', z.boolean()), + mimeType: z.string().optional() +}) + +export type MobileFileTabDiff = + | z.output + | z.output + +export { fileTabBinaryDiffSchema, fileTabTextDiffSchema } diff --git a/mobile/src/files/mobile-diff-image-preview.ts b/mobile/src/files/mobile-diff-image-preview.ts index 09d7cd2ef36..25d0244e111 100644 --- a/mobile/src/files/mobile-diff-image-preview.ts +++ b/mobile/src/files/mobile-diff-image-preview.ts @@ -3,8 +3,10 @@ import { buildImageDataUri } from '../../../src/shared/image-data-uri' // modifiedDeleted marks a proven deletion (modified side genuinely absent); an // empty modifiedContent alone can't, since a relay/SSH read failure also arrives // empty with modifiedIsBinary false. +// Kind is not named here: the reply reader admits any arm but `text`, so a host arm this build has +// not heard of reaches the same projection main's `kind !== 'text'` sent it to. export type MobileBinaryDiffResult = { - kind: 'binary' + kind?: string originalContent?: string modifiedContent?: string originalIsBinary?: boolean diff --git a/mobile/src/files/mobile-file-explorer-operations.ts b/mobile/src/files/mobile-file-explorer-operations.ts index d325fbea245..835243df950 100644 --- a/mobile/src/files/mobile-file-explorer-operations.ts +++ b/mobile/src/files/mobile-file-explorer-operations.ts @@ -1,5 +1,6 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc-operation' -import { rpcUncheckedPayloadReader } from '../transport/rpc-reader-payload' +import { rpcResultVariant } from '../transport/rpc-operation-result-reader' +import { fileDirectoryEntriesSchema, legacyFileListSchema } from './file-explorer-reply-schema' /** * The Files tab's directory read and the capped list it falls back to. @@ -9,6 +10,10 @@ import { rpcUncheckedPayloadReader } from '../transport/rpc-reader-payload' * answers `method_not_found`), and the `files.list` refusal supplies the message the screen shows. * No acceptance policy exposes a refusal code, so the panel reads the envelope's own error the way * `mobile-file-preview-operations.ts` does, and only these two consumers want one. + * + * A malformed *accepted* reply is what moves: the panel's own catch already turns a throw into the + * inline directory error it drew for a failed load, so an unreadable listing now names the reply + * instead of crashing the row builder one render later. */ export const fileDirectoryRead = bindDeferredRpcOperation( defineRpcOperation({ @@ -16,7 +21,7 @@ export const fileDirectoryRead = bindDeferredRpcOperation( method: 'files.readDir', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('directory-entries') + read: rpcResultVariant('directory-entries', fileDirectoryEntriesSchema) }) ) @@ -33,6 +38,6 @@ export const legacyFileListRead = bindDeferredRpcOperation( method: 'files.list', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('legacy-file-list') + read: rpcResultVariant('legacy-file-list', legacyFileListSchema) }) ) diff --git a/mobile/src/files/mobile-file-mutation-ownership.test.ts b/mobile/src/files/mobile-file-mutation-ownership.test.ts index f3e98dca9c6..84b4d077063 100644 --- a/mobile/src/files/mobile-file-mutation-ownership.test.ts +++ b/mobile/src/files/mobile-file-mutation-ownership.test.ts @@ -105,6 +105,31 @@ describe('mobile file mutation ownership', () => { ]) }) + // The three hostId states the reply reader keeps distinct, read end to end. An absent host is + // "none recorded" and captures local; an explicit null is a host that named something this client + // cannot place, and it refuses rather than sending the write to the runtime-local host. + it('captures local ownership for a workspace whose reply records no host', async () => { + const { client } = clientWithResponses([ + success({ capabilities: [FILE_MUTATION_OWNERSHIP_RUNTIME_CAPABILITY] }), + success({ worktree: {} }) + ]) + + await expect(captureMobileFileMutationOwnership(client, 'id:worktree-1')).resolves.toEqual({ + expectedExecutionHostId: 'local' + }) + }) + + it('refuses a workspace whose reply names an explicit null host', async () => { + const { client } = clientWithResponses([ + success({ capabilities: [FILE_MUTATION_OWNERSHIP_RUNTIME_CAPABILITY] }), + success({ worktree: { hostId: null } }) + ]) + + await expect(captureMobileFileMutationOwnership(client, 'id:worktree-1')).rejects.toThrow( + "Couldn't verify the SSH connection" + ) + }) + it('refuses older runtimes before reading or mutating workspace files', async () => { const { client, sendRequest } = clientWithResponses([success({ capabilities: [] })]) diff --git a/mobile/src/files/mobile-file-mutation-ownership.ts b/mobile/src/files/mobile-file-mutation-ownership.ts index 634f90f0637..9384df7801c 100644 --- a/mobile/src/files/mobile-file-mutation-ownership.ts +++ b/mobile/src/files/mobile-file-mutation-ownership.ts @@ -16,9 +16,16 @@ export type MobileFileMutationOwnership = SshMutationExpectation & { expectedExecutionHostId: 'local' | `ssh:${string}` } +// The two members the ownership gate routes on, as the reply reader hands them back. Absence and +// an explicit `null` stay distinct: the host omits `state` for a target it holds no connection for. +export type MobileFileMutationSshState = + | (Pick & { targetId?: string }) + | null + | undefined + export function buildMobileFileMutationOwnership( worktreeHostId: string | null | undefined, - sshState: SshConnectionState | null = null + sshState: MobileFileMutationSshState = null ): MobileFileMutationOwnership { const host = parseExecutionHostId(worktreeHostId) if (worktreeHostId !== undefined && !host) { @@ -52,24 +59,20 @@ export async function captureMobileFileMutationOwnership( { worktree }, { timeoutMs: FILE_MUTATION_TIMEOUT_MS } ) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. - const summary = fileOwnershipWorktreeRead.interpret(worktreeReply) as - | { hostId?: string | null } - | undefined + const summary = fileOwnershipWorktreeRead.interpret(worktreeReply) if (!summary) { throw new Error(SSH_OWNER_CHANGED_MESSAGE) } const host = parseExecutionHostId(summary.hostId) - let sshState: SshConnectionState | null = null + let sshState: MobileFileMutationSshState = null if (host?.kind === 'ssh') { const stateReply = await fileOwnershipSshStateRead.request( client, { targetId: host.targetId }, { timeoutMs: FILE_MUTATION_TIMEOUT_MS } ) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. - sshState = fileOwnershipSshStateRead.interpret(stateReply) as SshConnectionState | null + sshState = fileOwnershipSshStateRead.interpret(stateReply) } return buildMobileFileMutationOwnership(summary.hostId, sshState) } diff --git a/mobile/src/files/mobile-file-ownership-operations.ts b/mobile/src/files/mobile-file-ownership-operations.ts index 82084993729..520cccba42f 100644 --- a/mobile/src/files/mobile-file-ownership-operations.ts +++ b/mobile/src/files/mobile-file-ownership-operations.ts @@ -1,9 +1,15 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc-operation' -import { rpcUncheckedMemberReader } from '../transport/rpc-reader-payload' +import { rpcResultVariant } from '../transport/rpc-operation-result-reader' +import { + fileOwnershipSshStateSchema, + fileOwnershipWorktreeSchema +} from './file-ownership-reply-schema' // The three reads that pin which execution host owns a workspace before a file mutation is sent. // All three share one acceptance because the capture is all-or-nothing: any refusal aborts the -// mutation with the host's own message rather than letting a write land on the wrong host. +// mutation with the host's own message rather than letting a write land on the wrong host. An +// unreadable reply now aborts it the same way, with the method named, where main read a member off +// the cast payload and either threw a raw TypeError or captured an owner it had not checked. // The runtime status this gate needs is the one the Tasks screen already asks for, field for // field. A second operation would only be a second name for the same wire. @@ -16,7 +22,7 @@ export const fileOwnershipWorktreeRead = bindDeferredRpcOperation( method: 'worktree.show', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedMemberReader('worktree-summary', 'worktree') + read: rpcResultVariant('worktree-summary', fileOwnershipWorktreeSchema) }) ) @@ -27,7 +33,7 @@ export const fileOwnershipSshStateRead = bindDeferredRpcOperation( method: 'ssh.getState', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedMemberReader('ssh-connection-state', 'state') + read: rpcResultVariant('ssh-connection-state', fileOwnershipSshStateSchema) }) ) diff --git a/mobile/src/files/mobile-file-preview-operations.ts b/mobile/src/files/mobile-file-preview-operations.ts index ac679e61bce..eeb3c1fcb8c 100644 --- a/mobile/src/files/mobile-file-preview-operations.ts +++ b/mobile/src/files/mobile-file-preview-operations.ts @@ -1,5 +1,11 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc-operation' -import { rpcUncheckedPayloadReader } from '../transport/rpc-reader-payload' +import { rpcResultVariant } from '../transport/rpc-operation-result-reader' +import { + filePreviewImageSchema, + filePreviewTextSchema, + terminalArtifactWriteSchema, + terminalPathResolutionSchema +} from './file-preview-reply-schema' /** * The preview screen's reads and writes. @@ -10,9 +16,16 @@ import { rpcUncheckedPayloadReader } from '../transport/rpc-reader-payload' * the failure is a stale terminal-artifact grant worth refreshing. No acceptance policy exposes a * refusal code, and only these two consumers want one. * - * The payloads are unchecked here because the shape depends on the path, not on the method: - * `normalizeMobileFilePreviewResult` picks the image or text projection from the file name, which - * a module-level reader cannot see. + * What a *malformed* accepted reply does is what changes here. A skip's reader is consulted only + * after the policy has already admitted the reply, so an unreadable payload throws + * `RpcIncompatibleReplyError` naming the method; the preview screen's own try/catch runs it back + * through `previewError`, which lands on 'Unable to load preview' — the copy main already showed + * for an unreadable text payload, and a truer one than the 'Binary preview unavailable' main gave + * an unreadable image payload. + * + * The text and image readers are split by method rather than by path: `createMobileFilePreviewRequest` + * picks the method from `classifyMobileArtifact`, and `loadMobileFilePreview` normalizes with the + * same predicate over the same path, so each method has exactly one projection behind it. */ /** files.read for a preview. The tab doc asks the same method under a throwing policy. */ @@ -22,7 +35,7 @@ export const filePreviewTextRead = bindDeferredRpcOperation( method: 'files.read', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('file-preview') + read: rpcResultVariant('file-preview-text', filePreviewTextSchema) }) ) @@ -33,7 +46,7 @@ export const filePreviewImageRead = bindDeferredRpcOperation( method: 'files.readPreview', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('file-preview') + read: rpcResultVariant('file-preview-image', filePreviewImageSchema) }) ) @@ -43,7 +56,7 @@ export const terminalArtifactTextRead = bindDeferredRpcOperation( method: 'files.readTerminalArtifact', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('file-preview') + read: rpcResultVariant('file-preview-text', filePreviewTextSchema) }) ) @@ -53,7 +66,7 @@ export const terminalArtifactImageRead = bindDeferredRpcOperation( method: 'files.readTerminalArtifactPreview', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('file-preview') + read: rpcResultVariant('file-preview-image', filePreviewImageSchema) }) ) @@ -64,7 +77,7 @@ export const terminalArtifactWrite = bindDeferredRpcOperation( method: 'files.writeTerminalArtifact', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('artifact-written') + read: rpcResultVariant('artifact-written', terminalArtifactWriteSchema) }) ) @@ -75,7 +88,7 @@ export const terminalArtifactPathResolve = bindDeferredRpcOperation( method: 'files.resolveTerminalPath', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('terminal-path-resolution') + read: rpcResultVariant('terminal-path-resolution', terminalPathResolutionSchema) }) ) diff --git a/mobile/src/files/mobile-file-preview-request.test.ts b/mobile/src/files/mobile-file-preview-request.test.ts index 9c8692e5a78..ff1faa168ef 100644 --- a/mobile/src/files/mobile-file-preview-request.test.ts +++ b/mobile/src/files/mobile-file-preview-request.test.ts @@ -10,6 +10,7 @@ import { normalizeMobileFilePreviewResult, previewErrorFromRefusal } from './mobile-file-preview-response' +import { RPC_INCOMPATIBLE_REPLY_CODE } from '../transport/rpc-incompatible-reply-error' function ok(result: unknown): RpcSuccess { return { id: '1', ok: true, result, _meta: { runtimeId: 'runtime-1' } } @@ -501,7 +502,7 @@ describe('mobile-file-preview-request', () => { }) }) - it('reports a malformed refreshed artifact read instead of treating it as changed desktop content', async () => { + it('names the unreadable reply on a refreshed artifact read instead of treating it as changed desktop content', async () => { const client = clientWithResponses([ fail('terminal_file_grant_stale'), ok({ @@ -532,10 +533,9 @@ describe('mobile-file-preview-request', () => { '{"ok":false}', { baseContent: '{"ok":true}' } ) - ).resolves.toEqual({ - status: 'error', - message: 'Unable to load preview', - reconnect: false + ).rejects.toMatchObject({ + code: RPC_INCOMPATIBLE_REPLY_CODE, + method: 'files.readTerminalArtifact' }) expect(client.sendRequest).toHaveBeenCalledTimes(3) diff --git a/mobile/src/files/mobile-file-tab-doc-operations.ts b/mobile/src/files/mobile-file-tab-doc-operations.ts index 244d44e5e39..e14e5cecfc4 100644 --- a/mobile/src/files/mobile-file-tab-doc-operations.ts +++ b/mobile/src/files/mobile-file-tab-doc-operations.ts @@ -1,5 +1,12 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc-operation' -import { rpcUncheckedPayloadReader } from '../transport/rpc-reader-payload' +import { rpcResultVariant, rpcResultVariants } from '../transport/rpc-operation-result-reader' +import { + fileTabBinaryDiffSchema, + fileTabImageSchema, + fileTabTextDiffSchema, + fileTabTextSchema, + type MobileFileTabDiff +} from './file-tab-doc-reply-schema' /** * What a session file tab reads to render one document. @@ -9,17 +16,30 @@ import { rpcUncheckedPayloadReader } from '../transport/rpc-reader-payload' * while the preview screen renders the refusal as body copy. Two policies, two families, named * here and in mobile-file-preview-operations.ts so neither can drift onto the other. * - * The payloads stay unchecked: the tab picks its projection from the path, and moving a shape - * check into a reader would reject replies the tab renders today. + * The readers are stricter than the preview screen's for the same reason the policies differ: a tab + * publishes what it read into a typed ready document with no guard, so a member the preview screen + * normalizes is one the tab renders as `undefined`. An unreadable reply now reaches `readFileTab`'s + * catch as one named error instead of a property-read TypeError, and that catch already shows + * "Couldn't load file preview" for both. */ +/** + * The diff a staged or unstaged tab renders. + * + * Two variants, because the host's own result is a union whose arms require different members and + * whose arm set is a wire surface: a `kind` this build has not heard of takes the binary arm, which + * is the branch main's `kind !== 'text'` already sent it down. + */ export const fileTabDiffRead = bindDeferredRpcOperation( defineRpcOperation({ name: 'git.file-tab-diff', method: 'git.diff', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('file-tab-diff') + read: rpcResultVariants<'file-tab-text-diff' | 'file-tab-binary-diff', MobileFileTabDiff>([ + rpcResultVariant('file-tab-text-diff', fileTabTextDiffSchema), + rpcResultVariant('file-tab-binary-diff', fileTabBinaryDiffSchema) + ]) }) ) @@ -29,7 +49,7 @@ export const fileTabTextRead = bindDeferredRpcOperation( method: 'files.read', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('file-tab-text') + read: rpcResultVariant('file-tab-text', fileTabTextSchema) }) ) @@ -39,7 +59,7 @@ export const fileTabImageRead = bindDeferredRpcOperation( method: 'files.readPreview', acceptance: 'require-result-or-throw-message', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('file-tab-image') + read: rpcResultVariant('file-tab-image', fileTabImageSchema) }) ) diff --git a/mobile/src/files/mobile-file-tab-doc.ts b/mobile/src/files/mobile-file-tab-doc.ts index 1e9d43abbba..b22ae9fe396 100644 --- a/mobile/src/files/mobile-file-tab-doc.ts +++ b/mobile/src/files/mobile-file-tab-doc.ts @@ -1,7 +1,7 @@ import { buildImageDataUri } from '../../../src/shared/image-data-uri' import { classifyMobileArtifact } from '../session/mobile-artifact-kind' import { buildMobileDiffLines, type MobileDiffLine } from '../session/mobile-diff-lines' -import { mobileDiffImageDataUri, type MobileBinaryDiffResult } from './mobile-diff-image-preview' +import { mobileDiffImageDataUri } from './mobile-diff-image-preview' import { fileTabDiffRead, fileTabImageRead, @@ -37,10 +37,7 @@ export async function resolveMobileFileTabDoc( filePath: relativePath, staged: request.diffSource === 'staged' }) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. - const result = fileTabDiffRead.interpret(reply) as - | { kind: 'text'; originalContent: string; modifiedContent: string } - | MobileBinaryDiffResult + const result = fileTabDiffRead.interpret(reply) if (result.kind !== 'text') { // Render image diffs (add/modify/delete) from the base64 the host already // sends; only non-previewable binaries stay unavailable. @@ -57,12 +54,7 @@ export async function resolveMobileFileTabDoc( const artifactKind = classifyMobileArtifact(relativePath) if (artifactKind === 'image') { const preview = await fileTabImageRead.request(client, { worktree, relativePath }) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. - const result = fileTabImageRead.interpret(preview) as { - content: string - isImage?: boolean - mimeType?: string - } + const result = fileTabImageRead.interpret(preview) const dataUri = result.isImage ? buildImageDataUri(result.mimeType, result.content) : null if (!dataUri) { throw new Error('binary_file') @@ -71,12 +63,7 @@ export async function resolveMobileFileTabDoc( } const reply = await fileTabTextRead.request(client, { worktree, relativePath }) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. - const result = fileTabTextRead.interpret(reply) as { - content: string - truncated: boolean - byteLength: number - } + const result = fileTabTextRead.interpret(reply) if (artifactKind === 'html') { return { status: 'ready', kind: 'html', content: result.content } } diff --git a/mobile/src/host-screen/host-screen-operations.ts b/mobile/src/host-screen/host-screen-operations.ts index d0ddc342ac8..fa374ce6bc4 100644 --- a/mobile/src/host-screen/host-screen-operations.ts +++ b/mobile/src/host-screen/host-screen-operations.ts @@ -1,12 +1,21 @@ import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc-operation' +import { rpcResultVariant } from '../transport/rpc-operation-result-reader' import { - rpcUncheckedMemberReader, - rpcUncheckedPayloadReader -} from '../transport/rpc-reader-payload' + hostPlatformSchema, + hostRepoCatalogSchema, + hostScreenUnreadReplySchema, + hostSshTargetSummariesSchema, + hostViewSettingsSchema +} from './host-screen-reply-schema' // What the host screen reads to label its rows and to mirror the desktop's workspace view store. // Every read here is decorative: a refusal leaves the screen on what it already has and the next // refresh retries, so all of them skip rather than throw. +// +// A skip's reader runs only on a reply the policy already admitted, so an unreadable one throws +// rather than skipping. Both readers that project a list are inside the metadata refresh's own +// try/catch, which already treats a failed refresh as "retry on the next one"; the four writes read +// no reply body at all. host-screen-reply-schema.ts says which members each screen actually reads. export const hostRepoCatalogRead = bindDeferredRpcOperation( defineRpcOperation({ @@ -14,7 +23,7 @@ export const hostRepoCatalogRead = bindDeferredRpcOperation( method: 'repo.list', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('repo-catalog') + read: rpcResultVariant('repo-catalog', hostRepoCatalogSchema) }) ) @@ -25,7 +34,7 @@ export const hostSshTargetSummariesRead = bindDeferredRpcOperation( method: 'ssh.listTargetSummaries', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('ssh-target-summaries') + read: rpcResultVariant('ssh-target-summaries', hostSshTargetSummariesSchema) }) ) @@ -35,7 +44,7 @@ export const hostPlatformRead = bindDeferredRpcOperation( method: 'host.platform', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('host-platform') + read: rpcResultVariant('host-platform', hostPlatformSchema) }) ) @@ -51,7 +60,7 @@ export const hostViewSettingsRead = bindDeferredRpcOperation( method: 'ui.get', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedMemberReader('ui-view-settings', 'ui') + read: rpcResultVariant('ui-view-settings', hostViewSettingsSchema) }) ) @@ -62,7 +71,7 @@ export const hostViewSettingsWrite = bindDeferredRpcOperation( method: 'ui.set', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('ui-view-settings-written') + read: rpcResultVariant('ui-view-settings-written', hostScreenUnreadReplySchema) }) ) @@ -79,7 +88,7 @@ export const worktreePinWrite = bindDeferredRpcOperation( method: 'worktree.set', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('pin-written') + read: rpcResultVariant('pin-written', hostScreenUnreadReplySchema) }) ) @@ -90,7 +99,7 @@ export const worktreeRemove = bindDeferredRpcOperation( method: 'worktree.rm', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('worktree-removed') + read: rpcResultVariant('worktree-removed', hostScreenUnreadReplySchema) }) ) @@ -108,6 +117,6 @@ export const worktreeActivate = bindDeferredRpcOperation( method: 'worktree.activate', acceptance: 'success-result-or-skip', barrier: 'after-caller-barrier', - read: rpcUncheckedPayloadReader('worktree-activated') + read: rpcResultVariant('worktree-activated', hostScreenUnreadReplySchema) }) ) diff --git a/mobile/src/host-screen/host-screen-reply-schema.test.ts b/mobile/src/host-screen/host-screen-reply-schema.test.ts new file mode 100644 index 00000000000..0f36f86e3d4 --- /dev/null +++ b/mobile/src/host-screen/host-screen-reply-schema.test.ts @@ -0,0 +1,175 @@ +import { describe, expect, it } from 'vitest' +import type { RepoIcon } from '../../../src/shared/repo-icon' +import { NODE_PLATFORM_NAMES } from '../transport/mobile-runtime-host-platform' +import { + hostPlatformSchema, + hostRepoCatalogSchema, + hostSshTargetSummariesSchema, + hostViewSettingsSchema, + WORKSPACE_GROUP_BY_ARMS, + WORKSPACE_SORT_BY_ARMS +} from './host-screen-reply-schema' + +describe('host screen reply schemas', () => { + it('requires the repo list and drops a row no map could key', () => { + expect(hostRepoCatalogSchema.safeParse({}).success).toBe(false) + const parsed = hostRepoCatalogSchema.parse({ + repos: [{ id: 'r1', displayName: 'orca' }, { id: 'r2' }, { displayName: 'ghost' }] + }) + expect(parsed.map((repo) => repo.id)).toEqual(['r1']) + }) + + it('salvages badgeColor onto the generated swatch main fell back to', () => { + expect( + hostRepoCatalogSchema.parse({ repos: [{ id: 'r', displayName: 'o', badgeColor: 3 }] })[0] + ?.badgeColor + ).toBeUndefined() + }) + + it('keeps a known repo icon arm and degrades an unknown one to absent', () => { + const known = hostRepoCatalogSchema.parse({ + repos: [{ id: 'r', displayName: 'o', repoIcon: { type: 'emoji', emoji: 'x' } }] + }) + expect(known[0]?.repoIcon).toEqual({ type: 'emoji', emoji: 'x' }) + const unknown = hostRepoCatalogSchema.parse({ + repos: [{ id: 'r', displayName: 'o', repoIcon: { type: 'svg', markup: '' } }] + }) + // The row survives and draws the Folder default MobileRepoIcon already drew for an arm it + // could not match. + expect(unknown[0]?.id).toBe('r') + expect(unknown[0]?.repoIcon).toBeUndefined() + }) + + it('keeps an image icon whose source this build has never heard of', () => { + const [repo] = hostRepoCatalogSchema.parse({ + repos: [ + { + id: 'r', + displayName: 'o', + repoIcon: { + type: 'image', + src: 'https://example.invalid/a.png', + source: 'gitlab', + label: 'acme/orca' + } + } + ] + }) + // MobileRepoIcon reads src and label and never source, so narrowing source would have drawn a + // Folder where main drew the image. The member itself still reaches the row. + expect(repo?.repoIcon).toEqual({ + type: 'image', + src: 'https://example.invalid/a.png', + source: 'gitlab', + label: 'acme/orca' + }) + }) + + it('passes a host-id spelling through for getRepoExecutionHostId to judge', () => { + const [repo] = hostRepoCatalogSchema.parse({ + repos: [{ id: 'r', displayName: 'o', executionHostId: 'cloud:zone-a', connectionId: null }] + }) + expect(repo?.executionHostId).toBe('cloud:zone-a') + expect(repo?.connectionId).toBeNull() + }) + + it('degrades an unreadable ssh target list to the empty one that falls back to host ids', () => { + expect(hostSshTargetSummariesSchema.parse({})).toEqual([]) + expect(hostSshTargetSummariesSchema.parse({ targets: 'none' })).toEqual([]) + expect( + hostSshTargetSummariesSchema.parse({ + targets: [{ id: 't', label: 'T' }, { id: 'u' }, { label: 'V' }] + }) + ).toEqual([{ id: 't', label: 'T' }]) + }) + + it('answers the empty target list for a reply that is no object at all', () => { + // Why: the label write runs before the platform write in the same sequence, so a throw here + // would take the platform down with it. readSshTargets answered [] for every one of these. + for (const payload of [null, undefined, 'nope', 42, []]) { + expect(hostSshTargetSummariesSchema.parse(payload)).toEqual([]) + } + }) + + it('reads only a platform Node could have reported', () => { + expect(hostPlatformSchema.parse({ platform: 'win32' })).toBe('win32') + expect(hostPlatformSchema.parse({ platform: 'plan9' })).toBeNull() + expect(hostPlatformSchema.parse({ platform: '' })).toBeNull() + expect(hostPlatformSchema.parse({})).toBeNull() + for (const payload of [null, undefined, 'nope', 42]) { + expect(hostPlatformSchema.parse(payload)).toBeNull() + } + }) + + it('requires the ui member main read with a bare property access', () => { + expect(hostViewSettingsSchema.safeParse({}).success).toBe(false) + expect(hostViewSettingsSchema.safeParse(null).success).toBe(false) + expect(hostViewSettingsSchema.parse({ ui: {} })).toEqual({}) + }) + + it('degrades an unknown grouping or sort arm to absent so the local mode is kept', () => { + const parsed = hostViewSettingsSchema.parse({ + ui: { groupBy: 'agent', sortBy: 'stars', hideSleepingWorkspaces: 'yes' } + }) + expect(parsed.groupBy).toBeUndefined() + expect(parsed.sortBy).toBeUndefined() + expect(parsed.hideSleepingWorkspaces).toBeUndefined() + }) + + it('keeps the known view arms and the lists the screen adopts', () => { + const parsed = hostViewSettingsSchema.parse({ + ui: { + groupBy: 'workspace-status', + sortBy: 'recent', + filterRepoIds: ['r1'], + collapsedGroups: [], + workspaceStatuses: [{ id: 'active', label: 'Active' }] + } + }) + expect(parsed.groupBy).toBe('workspace-status') + expect(parsed.sortBy).toBe('recent') + expect(parsed.filterRepoIds).toEqual(['r1']) + expect(parsed.workspaceStatuses).toEqual([{ id: 'active', label: 'Active' }]) + }) + + it('salvages a non-array status catalog rather than handing a string to the group lookups', () => { + expect( + hostViewSettingsSchema.parse({ ui: { workspaceStatuses: 'active' } }).workspaceStatuses + ).toBeUndefined() + }) +}) + +describe('the closed arm sets are the desktop unions', () => { + // The arm lists are pinned to the desktop unions in the schema modules, where tsc looks; these + // loops prove every pinned arm survives the parse, not just the ones the tests above pick. + it('keeps every platform Node can report', () => { + for (const platform of NODE_PLATFORM_NAMES) { + expect(hostPlatformSchema.parse({ platform })).toBe(platform) + } + }) + + it('keeps every grouping and sort arm the desktop persists', () => { + for (const groupBy of WORKSPACE_GROUP_BY_ARMS) { + for (const sortBy of WORKSPACE_SORT_BY_ARMS) { + expect(hostViewSettingsSchema.parse({ ui: { groupBy, sortBy } })).toMatchObject({ + groupBy, + sortBy + }) + } + } + }) + + it('keeps every repo icon arm the desktop draws', () => { + const icons: Record> = { + lucide: { type: 'lucide', name: 'Folder' }, + emoji: { type: 'emoji', emoji: '🐳' }, + image: { type: 'image', src: 'data:,x' } + } + for (const repoIcon of Object.values(icons)) { + const [row] = hostRepoCatalogSchema.parse({ + repos: [{ id: 'r', displayName: 'r', repoIcon }] + }) + expect(row?.repoIcon).toMatchObject({ type: repoIcon.type }) + } + }) +}) diff --git a/mobile/src/host-screen/host-screen-reply-schema.ts b/mobile/src/host-screen/host-screen-reply-schema.ts new file mode 100644 index 00000000000..ddc72e29966 --- /dev/null +++ b/mobile/src/host-screen/host-screen-reply-schema.ts @@ -0,0 +1,181 @@ +import { z } from 'zod' +import type { PersistedUIState } from '../../../src/shared/persisted-ui-state-types' +import type { RepoIcon } from '../../../src/shared/repo-icon' +import { hostUnionArms, salvagedOptional, salvagingArray } from '../../../src/shared/zod-salvage' +import { NODE_PLATFORM_NAMES } from '../transport/mobile-runtime-host-platform' + +// The closed arm sets on this screen are the desktop's own unions, pinned through hostUnionArms so +// an arm the desktop adds or drops fails tsc here rather than degrading silently on the phone. +export const WORKSPACE_GROUP_BY_ARMS = hostUnionArms({ + none: true, + 'workspace-status': true, + repo: true, + 'pr-status': true +}) +export const WORKSPACE_SORT_BY_ARMS = hostUnionArms({ + name: true, + smart: true, + recent: true, + repo: true, + manual: true +}) + +// One branch per RepoIcon arm; `satisfies` over the mapped union fails tsc on a missing or stale arm. +const repoIconBranches = { + lucide: z.looseObject({ type: z.literal('lucide'), name: z.string() }), + emoji: z.looseObject({ type: z.literal('emoji'), emoji: z.string() }), + image: z.looseObject({ + type: z.literal('image'), + src: z.string(), + label: salvagedOptional('label', z.string()) + }) +} satisfies Readonly> + +// What the host screen reads to label its rows and to mirror the desktop's workspace view store. +// Checked against src/main/runtime/rpc/methods/repo.ts:29, ssh.ts:55, host-capabilities.ts:8 and +// client-ui.ts:60/65, and the shared records they return: Repo in src/shared/repo-types.ts:42 and +// PersistedUIState's workspace-view subset in mobile/src/worktree/workspace-view-settings.ts:14. + +/** + * The host's repo catalog, narrowed to what the label maps are built from. + * + * `id` and `displayName` are required and a row without either drops: `displayName` is the key of + * four Maps and the argument `repoColor` hashes with `name.charCodeAt` — a row without one was a + * TypeError that took the whole metadata refresh with it — and `id` is the Map value the workspace + * rows resolve their host through. + * + * `badgeColor` sits behind main's own `||` fallback to `repoColor`, so a salvaged member draws the + * same swatch. `repoIcon` is declared as the three arms MobileRepoIcon renders, and an arm this + * build has not heard of degrades to absent — which is the Folder default that component already + * drew for an arm it could not match, so the row keeps its label either way. + * The image arm deliberately stops at `src` and `label`: those are the members the component reads, + * and `source` — which it never reads — is left to pass through. Declaring it as the four arms + * `RepoIconImageSource` spells today would have dropped the WHOLE icon for a source a later host + * adds, drawing a Folder where main drew the image; passthrough keeps the member on the object + * verbatim, which is also what `settings-repo-metadata-icons` records. + * `connectionId` and `executionHostId` are declared as plain strings rather than as + * the host-id template union they are typed with: the union is a wire surface, and + * `getRepoExecutionHostId` — which is what every read of them goes through — already answers `local` + * for a spelling it cannot parse. Narrowing them here would refuse a newer host's own rows. + */ +export const hostRepoCatalogSchema = z + .looseObject({ + repos: salvagingArray( + z.looseObject({ + id: z.string(), + displayName: z.string(), + badgeColor: salvagedOptional('badgeColor', z.string()), + repoIcon: salvagedOptional( + 'repoIcon', + z.union([repoIconBranches.lucide, repoIconBranches.emoji, repoIconBranches.image]) + ), + connectionId: salvagedOptional('connectionId', z.string().nullable()), + executionHostId: salvagedOptional('executionHostId', z.string().nullable()) + }) + ) + }) + .transform((reply) => reply.repos) + +/** + * The SSH target labels a mixed-host catalog names its rows with. + * + * The rows the label builder keeps are exactly the rows with a string `id` and `label`, so the + * filter that used to sit in `readSshTargets` is the schema now and a row without either drops. + * `targets` itself is salvaged rather than required because main answered `[]` for a reply without + * it, and a `[]` here is what makes the labels degrade to host ids — the documented behaviour for a + * host that predates the method. + * + * Total, like the reader it replaces: `readSshTargets` answered `[]` for any payload at all, and + * the caller writes the labels before it reads the platform, so a throw here would also skip the + * platform write. The `.catch` keeps a non-object reply degrading exactly where main degraded. + */ +export const hostSshTargetSummariesSchema = z + .looseObject({ + targets: salvagedOptional( + 'targets', + salvagingArray(z.looseObject({ id: z.string(), label: z.string() })) + ) + }) + .transform((reply) => reply.targets ?? []) + .catch(() => []) + +/** + * The paired host's own platform. + * + * Salvaged to absent, which reads as null — main's own answer for a non-string or an empty one + * (`typeof platform === 'string' && platform`), and the value that keeps the phone's platform from + * naming the desktop. The arm set is closed over Node's platform domain rather than over anything + * Orca versions: the handler returns `process.platform` and nothing else, and a string outside that + * set names no path convention this client could apply. + * + * Total for the same reason as the SSH targets above: `readHostPlatform` answered `null` for any + * payload, so a non-object reply degrades here instead of throwing past the label write. + */ +export const hostPlatformSchema = z + .looseObject({ platform: salvagedOptional('platform', z.enum(NODE_PLATFORM_NAMES)) }) + .transform((reply) => reply.platform ?? null) + .catch(() => null) + +/** + * The desktop's shared workspace view settings, read off `ui.get`'s `ui` member. + * + * Nothing is required: applyDesktopViewSettings reads every member behind `??` or a mapping table + * that answers null for an arm it does not know, so a salvaged member leaves the local value in + * place — which is exactly what main did for an absent one. `groupBy` and `sortBy` are closed arm + * sets that degrade to absent for the same reason: every read is a lookup that already fell back to + * the current mode for an arm it could not map, so nothing is withheld that the reply granted. + * + * `workspaceStatuses` keeps its rows opaque — coerceMobileWorkspaceStatuses only counts them — but + * the container is checked, because main handed a non-array straight into the status catalog and + * every group lookup then read `.find` off a string. + * + * The `ui` member itself is required. Main read it off the payload with a bare property access that + * threw a TypeError on a null result, and the host screen's own try/catch is where that throw has + * always landed; an incompatible reply reaches the same catch with the method named. + */ +export const hostViewSettingsSchema = z + .looseObject({ + ui: z.looseObject({ + groupBy: salvagedOptional('groupBy', z.enum(WORKSPACE_GROUP_BY_ARMS)), + sortBy: salvagedOptional('sortBy', z.enum(WORKSPACE_SORT_BY_ARMS)), + hideSleepingWorkspaces: salvagedOptional('hideSleepingWorkspaces', z.boolean()), + hideDefaultBranchWorkspace: salvagedOptional('hideDefaultBranchWorkspace', z.boolean()), + alwaysShowDefaultBranchWorkspace: salvagedOptional( + 'alwaysShowDefaultBranchWorkspace', + z.boolean() + ), + filterRepoIds: salvagedOptional('filterRepoIds', z.array(z.string())), + collapsedGroups: salvagedOptional('collapsedGroups', z.array(z.string())), + workspaceStatuses: salvagedOptional( + 'workspaceStatuses', + z.array(z.looseObject({ id: z.string(), label: z.string() })) + ) + }) + }) + .transform((reply) => reply.ui) + +/** + * The four host-list writes whose reply body no call site reads. + * + * The `ui.set` patch, the pin write, the row delete and the activate ping are all decided by the + * acceptance verdict alone — the pin write never interprets its reply at all, and the delete reads + * `accepted` and nothing else. + * + * `worktree.activate` is the one of the four whose payload a *second* consumer looks at, and it is + * deliberately left opaque: headlessActivationNeedsHostRenderer is a total guard over `unknown` + * (worktree-activation-result.ts:1), and the session route's second report site + * (use-mobile-session-startup.ts:170) reports from inside a fire-and-forget `void (async …)()` with + * no catch of its own, so a reader that could throw would turn an unreadable activation into an + * unhandled rejection *and* skip the terminal fetch below it, where main showed no toast and + * fetched. This schema staying total is what holds that site safe; the first report site + * (:141) is chained `.then(…).catch(…)` and would survive a throw. + */ +export const hostScreenUnreadReplySchema = z.unknown() + +/** One decoded catalog icon: the members MobileRepoIcon reads, with the rest passed through. */ +export type MobileHostRepoIcon = NonNullable< + z.output[number]['repoIcon'] +> + +/** What MobileRepoIcon renders: a decoded catalog icon, or the `RepoIcon` a worktree row carries. */ +export type MobileRenderableRepoIcon = MobileHostRepoIcon | RepoIcon diff --git a/mobile/src/host-screen/use-host-repo-metadata.ts b/mobile/src/host-screen/use-host-repo-metadata.ts index d87d917b467..0987aed14b0 100644 --- a/mobile/src/host-screen/use-host-repo-metadata.ts +++ b/mobile/src/host-screen/use-host-repo-metadata.ts @@ -5,7 +5,6 @@ import { setCachedRepos } from '../cache/repo-cache' import type { RpcAcceptedResult } from '../transport/rpc-accepted-result' import type { RpcClient } from '../transport/rpc-client' import type { ConnectionState, RpcResponse } from '../transport/types' -import type { RepoSummary } from '../worktree/host-worktree-rpc-types' import { repoColor } from '../worktree/repo-color' import { buildHostLabelById, @@ -20,8 +19,6 @@ import type { HostScreenState } from './use-host-screen-state' const REPO_METADATA_REFRESH_MS = 60_000 -type SshTargetSummaryRow = { id: string; label: string } - async function settledMetadataReply(send: () => Promise): Promise { try { return await send() @@ -32,10 +29,10 @@ async function settledMetadataReply(send: () => Promise): Promise( reply: RpcResponse | null, - interpret: (reply: RpcResponse) => RpcAcceptedResult -): unknown { + interpret: (reply: RpcResponse) => RpcAcceptedResult +): Value | null { if (!reply) { return null } @@ -43,25 +40,6 @@ function acceptedMetadata( return verdict.accepted ? verdict.value : null } -function readSshTargets(result: unknown): SshTargetSummaryRow[] { - const targets = (result as { targets?: unknown } | null)?.targets - if (!Array.isArray(targets)) { - return [] - } - return targets.filter( - (target): target is SshTargetSummaryRow => - typeof target === 'object' && - target !== null && - typeof (target as SshTargetSummaryRow).id === 'string' && - typeof (target as SshTargetSummaryRow).label === 'string' - ) -} - -function readHostPlatform(result: unknown): NodeJS.Platform | null { - const platform = (result as { platform?: unknown } | null)?.platform - return typeof platform === 'string' && platform ? (platform as NodeJS.Platform) : null -} - function readHostSettingOverrides(result: unknown): unknown { // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. return (result as { hostSettingOverrides?: unknown } | null)?.hostSettingOverrides @@ -118,13 +96,12 @@ export function useHostRepoMetadata(args: { if (!repos || !repos.accepted) { return } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary. - const repoResult = repos.value as { repos: RepoSummary[] } + const catalog = repos.value repoMetadataFetchedAtRef.current = Date.now() - setCachedRepos(requestHostId, repoResult.repos) + setCachedRepos(requestHostId, catalog) setRepoColorsByName( new Map( - repoResult.repos.map((repo) => [ + catalog.map((repo) => [ repo.displayName, repo.badgeColor || repoColor(repo.displayName) ]) @@ -132,17 +109,17 @@ export function useHostRepoMetadata(args: { ) setRepoIconsByName( new Map( - repoResult.repos.flatMap((repo) => + catalog.flatMap((repo) => repo.repoIcon ? [[repo.displayName, repo.repoIcon] as const] : [] ) ) ) - setRepoIdsByName(new Map(repoResult.repos.map((repo) => [repo.displayName, repo.id]))) - setRepoHostIdByRepoId(buildRepoHostIdByRepoId(repoResult.repos)) + setRepoIdsByName(new Map(catalog.map((repo) => [repo.displayName, repo.id]))) + setRepoHostIdByRepoId(buildRepoHostIdByRepoId(catalog)) // Why: rows only name their host when the list spans hosts, so a single-host // catalog never pays for the label lookups. Counted over repos, not the id-keyed // map: one repo id registered on two hosts is two hosts. - const hostIds = new Set(repoResult.repos.map((repo) => getRepoExecutionHostId(repo))) + const hostIds = new Set(catalog.map((repo) => getRepoExecutionHostId(repo))) if (hostIds.size > 1) { const [sshTargets, hostSettings, hostPlatform] = await Promise.all([ settledMetadataReply(() => hostSshTargetSummariesRead.request(requestClient)), @@ -157,17 +134,14 @@ export function useHostRepoMetadata(args: { : null setHostLabelById( buildHostLabelById({ - sshTargets: readSshTargets( - acceptedMetadata(sshTargets, hostSshTargetSummariesRead.interpret) - ), + sshTargets: + acceptedMetadata(sshTargets, hostSshTargetSummariesRead.interpret) ?? [], hostSettingOverrides: readHostSettingOverrides( hostSettingsResult?.accepted ? hostSettingsResult.value : undefined ) }) ) - setHostPlatform( - readHostPlatform(acceptedMetadata(hostPlatform, hostPlatformRead.interpret)) - ) + setHostPlatform(acceptedMetadata(hostPlatform, hostPlatformRead.interpret) ?? null) } } while (fetchRepoMetadataPendingRef.current.has(requestClient)) } catch { diff --git a/mobile/src/host-screen/use-host-screen-state.ts b/mobile/src/host-screen/use-host-screen-state.ts index ca6bd0d85e7..9f6f8358c6f 100644 --- a/mobile/src/host-screen/use-host-screen-state.ts +++ b/mobile/src/host-screen/use-host-screen-state.ts @@ -1,6 +1,5 @@ import { useRef, useState } from 'react' import type { ExecutionHostId } from '../../../src/shared/execution-host' -import type { RepoIcon } from '../../../src/shared/repo-icon' import type { WorkspaceStatusDefinition } from '../../../src/shared/worktree/types' import { getCachedWorktrees } from '../cache/worktree-cache' import { createInitialHostRouteActionState } from '../host-route-action-state' @@ -13,6 +12,7 @@ import type { MobileViewState } from '../worktree/workspace-view-settings' import type { FilterState, Worktree } from '../worktree/workspace-list-sections' +import type { MobileHostRepoIcon } from './host-screen-reply-schema' export function useHostScreenState(hostId: string | undefined, action: string | undefined) { const [initialCache] = useState(() => @@ -38,7 +38,7 @@ export function useHostScreenState(hostId: string | undefined, action: string | string | null >(null) const [repoColorsByName, setRepoColorsByName] = useState>(new Map()) - const [repoIconsByName, setRepoIconsByName] = useState>(new Map()) + const [repoIconsByName, setRepoIconsByName] = useState>(new Map()) const [hostName, setHostName] = useState('') const [error, setError] = useState('') const [lastKnownWorktrees, setLastKnownWorktrees] = useState(initialCache ?? []) diff --git a/mobile/src/session/MobileNativeChatComposer.tsx b/mobile/src/session/MobileNativeChatComposer.tsx index 20c43aa6c8b..29efb22f547 100644 --- a/mobile/src/session/MobileNativeChatComposer.tsx +++ b/mobile/src/session/MobileNativeChatComposer.tsx @@ -60,7 +60,7 @@ type Props = { onMicPress?: () => void micActive?: boolean /** Dictation trigger style — 'hold' uses press-in/out, 'toggle' uses tap. */ - dictationMode?: 'toggle' | 'hold' + dictationMode?: string onMicPressIn?: () => void onMicPressOut?: () => void disabled?: boolean diff --git a/mobile/src/session/MobileNativeChatOverlay.tsx b/mobile/src/session/MobileNativeChatOverlay.tsx index a72b91ff29c..65f25e4ea24 100644 --- a/mobile/src/session/MobileNativeChatOverlay.tsx +++ b/mobile/src/session/MobileNativeChatOverlay.tsx @@ -16,7 +16,7 @@ type Props = { images: MobileNativeChatImageAttachments onMicPress: () => void micActive: boolean - dictationMode: 'toggle' | 'hold' + dictationMode: string | undefined onMicPressIn: () => void onMicPressOut: () => void inputLockReason: MobileNativeChatInputLockReason | null diff --git a/mobile/src/session/MobileNativeChatView.tsx b/mobile/src/session/MobileNativeChatView.tsx index 49f20fa5d5d..6a51601d151 100644 --- a/mobile/src/session/MobileNativeChatView.tsx +++ b/mobile/src/session/MobileNativeChatView.tsx @@ -93,7 +93,7 @@ type Props = { isAttaching?: boolean onMicPress?: () => void micActive?: boolean - dictationMode?: 'toggle' | 'hold' + dictationMode?: string onMicPressIn?: () => void onMicPressOut?: () => void inputLockReason?: MobileNativeChatInputLockReason | null diff --git a/mobile/src/session/MobileTerminalInputActions.tsx b/mobile/src/session/MobileTerminalInputActions.tsx index 4a42b327832..37f69ae3bb3 100644 --- a/mobile/src/session/MobileTerminalInputActions.tsx +++ b/mobile/src/session/MobileTerminalInputActions.tsx @@ -12,7 +12,7 @@ type MobileTerminalInputActionsProps = { readonly canSend: boolean readonly isAttaching: boolean readonly dictation: DictationState - readonly dictationMode: 'toggle' | 'hold' + readonly dictationMode: string | undefined readonly buttonStyle: StyleProp readonly activeButtonStyle: StyleProp readonly disabledButtonStyle: StyleProp diff --git a/mobile/src/session/mobile-markdown-disk-fallback.ts b/mobile/src/session/mobile-markdown-disk-fallback.ts index 9216d526ca2..910fb072564 100644 --- a/mobile/src/session/mobile-markdown-disk-fallback.ts +++ b/mobile/src/session/mobile-markdown-disk-fallback.ts @@ -9,9 +9,11 @@ export function shouldReadMarkdownFromDiskAfterReadTabFailure(response: RpcFailu ) } +// `truncated` is optional because the preview reader salvages it: an absent flag reads as not +// truncated here, which is the branch main took for a reply that omitted it. export function buildMarkdownDiskFallbackDoc(args: { content: string - truncated: boolean + truncated: boolean | undefined tabIsDirty: boolean }) { const readOnlyReason = args.truncated diff --git a/mobile/src/session/mobile-session-route-parity.test.ts b/mobile/src/session/mobile-session-route-parity.test.ts index 4ae381c4ed9..8b877286997 100644 --- a/mobile/src/session/mobile-session-route-parity.test.ts +++ b/mobile/src/session/mobile-session-route-parity.test.ts @@ -73,8 +73,12 @@ const HEAD_CALLBACK_IDENTITY_SHA256 = // `=== true` to match the other four sites reading the same verdict. Refreshed in step 7 for the // reply casts the checked readers made unnecessary — the markdown tab doc, the worktree record's // `diffComments` and the browser tab's page id are typed by their schemas now. Refreshed once more -// on the merge, for the display-mode toggle whose send became `terminalDisplayModeSet`. -const HEAD_CALLBACK_BODY_SHA256 = 'e3b41d4ab755be2ac2b8c268f3b94a5ec91f620233b5761707bbd1791d106f95' +// on the merge, for the display-mode toggle whose send became `terminalDisplayModeSet`. Refreshed +// for the files domain's step 7, which retired the markdown disk fallback's `{ content, truncated, +// byteLength }` cast: the preview reader checks the content and salvages the flag, so `readMarkdownTab` +// reads `fallback.value` directly. The dictation-mode refresh is main's own body again — it forwards +// whatever mode the reply carried, so an absent one leaves the mic as inert as main left it. +const HEAD_CALLBACK_BODY_SHA256 = 'ceba525103ccac47df766063d58593ba083d59785f86257d849e355669ed47ae' // Refreshed for the startup effect: both `worktree.activate` sends became `worktreeActivate`, and // the sleeping-agent check reads that operation's verdict instead of the reply envelope. Refreshed // again when the reporter took the reply and interpreted it itself, retiring the hand-built diff --git a/mobile/src/session/use-mobile-session-document-readers.ts b/mobile/src/session/use-mobile-session-document-readers.ts index 1da354f431d..857d1981570 100644 --- a/mobile/src/session/use-mobile-session-document-readers.ts +++ b/mobile/src/session/use-mobile-session-document-readers.ts @@ -52,12 +52,7 @@ export function useMobileSessionDocumentReaders(scope: MobileSessionTabApplicati if (!fallback.accepted) { throw new Error('Unable to read markdown') } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: main cast this payload unread; the shared preview reader hands it back whole. - const fileResult = fallback.value as { - content: string - truncated: boolean - byteLength: number - } + const fileResult = fallback.value setMarkdownDocs((prev) => new Map(prev).set( tab.id, diff --git a/mobile/src/session/use-mobile-session-screen-state.ts b/mobile/src/session/use-mobile-session-screen-state.ts index 6e2f82124b3..2d7596d8971 100644 --- a/mobile/src/session/use-mobile-session-screen-state.ts +++ b/mobile/src/session/use-mobile-session-screen-state.ts @@ -119,7 +119,9 @@ export function useMobileSessionScreenState(scope: MobileSessionFoundationModel) const [canPaste, setCanPaste] = useState(false) const [showDictationSetup, setShowDictationSetup] = useState(false) // 'hold' = press-and-hold mic, 'toggle' = tap-to-start/stop; mirrors Settings ▸ Voice ▸ Dictation Mode. - const [dictationMode, setDictationMode] = useState<'toggle' | 'hold'>('toggle') + // Holds the host's spelling verbatim, and undefined once a setup reply arrives without one: only + // the two arms below bind mic handlers, so anything else leaves the button as inert as it starts. + const [dictationMode, setDictationMode] = useState('toggle') const [toastMessage, setToastMessage] = useState(null) const toastOpacityRef = useRef(new Animated.Value(0)) const toastHideTimerRef = useRef | null>(null) diff --git a/mobile/src/test-support/rpc-recording/mutants/operation-mutations.ts b/mobile/src/test-support/rpc-recording/mutants/operation-mutations.ts index 8b2843bed62..38867e23bb3 100644 --- a/mobile/src/test-support/rpc-recording/mutants/operation-mutations.ts +++ b/mobile/src/test-support/rpc-recording/mutants/operation-mutations.ts @@ -127,11 +127,19 @@ export const OPERATION_MUTATIONS = { before: 'linearConnected: linear?.connected === true', after: 'linearConnected: linear !== null' }, - // Reads the host platform from the wrong field of the host.platform result. + // Reads the host platform from the wrong field of the host.platform result. Re-anchored where + // step 7 moved the read: the hand-rolled `readHostPlatform` became the reply schema's own + // projection, so the anchor is that projection. The defect it injects — rows labelled with a + // platform the host never reported — is unchanged. 'repo-metadata-platform': { - file: 'use-host-repo-metadata.ts', - before: 'const platform = (result as { platform?: unknown } | null)?.platform', - after: 'const platform = (result as { hostPlatform?: unknown } | null)?.hostPlatform' + file: 'host-screen-reply-schema.ts', + before: ` .looseObject({ platform: salvagedOptional('platform', z.enum(NODE_PLATFORM_NAMES)) }) + .transform((reply) => reply.platform ?? null)`, + after: ` .looseObject({ + platform: salvagedOptional('platform', z.enum(NODE_PLATFORM_NAMES)), + hostPlatform: salvagedOptional('hostPlatform', z.enum(NODE_PLATFORM_NAMES)) + }) + .transform((reply) => reply.hostPlatform ?? null)` }, // Hydrates the runtime task settings from the envelope rather than the accepted value. 'task-hydration-envelope': { diff --git a/mobile/src/test-support/rpc-recording/mutants/reply-schema-mutations.ts b/mobile/src/test-support/rpc-recording/mutants/reply-schema-mutations.ts new file mode 100644 index 00000000000..e9374d1a01d --- /dev/null +++ b/mobile/src/test-support/rpc-recording/mutants/reply-schema-mutations.ts @@ -0,0 +1,89 @@ +import type { OperationMutation } from '../operation-module-loader' + +/** + * Mutant evidence for the checked reply readers, in the same shape as the adapter-family mutations + * beside it and kept apart from them for one reason: a reader mutation is not killed by a pilot + * scenario's visible state. A pilot serves a *good* reply, and a schema that has stopped checking a + * member still reads a good reply exactly as before. What kills these is the malformed partition of + * a matrix golden, the schema's own unit pin, or a consumer pin — so they are applied by hand and + * the gate that caught each is named below rather than being driven by `pilot-mutants.test.ts`. + * + * Nothing on the recording path imports this file, which `mutant-seam.test.ts` holds. + * + * Two of the first three survived their first run, and both survivals were defects in the gates + * rather than in the readers: + * + * - `file-tab-text-content-optional` survived because the unit pin dropped one required member at + * a time only in prose: it asserted `{ content, truncated }` and `{ content, byteLength }` were + * refused, and each of those is refused by the *other* missing member. The matrix golden masked + * it the same way, because `result-absent` fails on all three at once. The pin now drops exactly + * one member per iteration, and the same pattern was applied to the preview text schema and the + * legacy file list. + * - `ownership-host-id-null-collapse` survived because no golden serves an explicit `null` hostId: + * `files-ownership-local` omits the member instead. A tri-state is a consumer property rather + * than a projection one, so the gate added for it is a consumer pin that captures all three + * states end to end. + */ +export const REPLY_SCHEMA_MUTATIONS = { + /** + * (a) Loosens a member the file tab publishes into its ready document with no guard. + * + * Killed by `src/files/file-tab-doc-reply-schema.test.ts` — "requires the three members the tab + * publishes into a ready document" and "requires the image content buildImageDataUri calls + * replace on", because the anchor appears on both schemas in that file. Not killed by any + * golden: every matrix partition that omits `content` omits its two siblings as well. + */ + 'file-tab-text-content-optional': { + file: 'file-tab-doc-reply-schema.ts', + before: ' content: z.string(),', + after: ' content: z.string().optional(),' + }, + /** + * (b) Puts the directory listing back on the unchecked reader it replaced. + * + * Killed twice. `matrix-files.explorer-screen-files.readdir-1` diverges at + * `files-explorer-legacy-fallback.result-absent:legacy-listed`, field + * `state.elements.Pressable`: the Files tab draws the empty tree and no retry again instead of + * the named error row. `unchecked-rpc-reader-boundary.test.ts` fails in the other direction — + * "has no unlisted file holding an unchecked reader" — because the inventory line for this file + * is gone. + */ + 'file-directory-read-unchecked': { + file: 'mobile-file-explorer-operations.ts', + before: " read: rpcResultVariant('directory-entries', fileDirectoryEntriesSchema)", + after: " read: rpcUncheckedPayloadReader('directory-entries')" + }, + /** + * (c) Collapses the hostId tri-state, which is the one member on this branch where absence and an + * explicit null mean different things: absent is "no host recorded" and captures local, null is a + * host this client cannot place and refuses. The collapse sends a file write to the runtime-local + * host for a workspace whose owner the reply did not establish. + * + * Killed by `src/files/mobile-file-mutation-ownership.test.ts` — "refuses a workspace whose reply + * names an explicit null host" resolves to a local capture instead of rejecting. + */ + 'ownership-host-id-null-collapse': { + file: 'mobile-file-mutation-ownership.ts', + before: ' return buildMobileFileMutationOwnership(summary.hostId, sshState)', + after: ' return buildMobileFileMutationOwnership(summary.hostId ?? undefined, sshState)' + }, + /** + * (d) Puts the closed image-source enum back on the repo icon — the one arm set on this branch + * that was narrower than what the wire can carry. No mobile consumer reads `source`, so the + * enum's only effect is that an icon whose source a later host adds fails the union arm, drops + * whole, and draws the Folder default where main drew the image. + * + * Killed by `src/host-screen/host-screen-reply-schema.test.ts` — "keeps an image icon whose + * source this build has never heard of". No golden kills it, and that is the point: this is the + * member `settings-repo-metadata-icons` was recorded for, and a fixture can only carry a source + * that exists today, so the future-arm case stays a unit property. + */ + 'repo-icon-source-closed': { + file: 'host-screen-reply-schema.ts', + before: ' src: z.string(),', + after: + " src: z.string(),\n source: z.enum(['upload', 'file', 'favicon', 'github'])," + } +} as const satisfies Record> + +export type ReplySchemaMutation = keyof typeof REPLY_SCHEMA_MUTATIONS diff --git a/mobile/src/transport/mobile-runtime-host-platform.ts b/mobile/src/transport/mobile-runtime-host-platform.ts index cf7b13aa283..03cbcd390a1 100644 --- a/mobile/src/transport/mobile-runtime-host-platform.ts +++ b/mobile/src/transport/mobile-runtime-host-platform.ts @@ -1,16 +1,21 @@ -const NODE_PLATFORMS = new Set([ - 'aix', - 'android', - 'darwin', - 'freebsd', - 'haiku', - 'linux', - 'openbsd', - 'sunos', - 'win32', - 'cygwin', - 'netbsd' -]) +import { hostUnionArms } from '../../../src/shared/zod-salvage' + +/** Node's own platform domain, pinned to @types/node's union: an arm added or dropped there fails tsc here. */ +export const NODE_PLATFORM_NAMES = hostUnionArms({ + aix: true, + android: true, + darwin: true, + freebsd: true, + haiku: true, + linux: true, + openbsd: true, + sunos: true, + win32: true, + cygwin: true, + netbsd: true +}) + +const NODE_PLATFORMS = new Set(NODE_PLATFORM_NAMES) export function readMobileRuntimeHostPlatform(statusResult: unknown): NodeJS.Platform | null { const hostPlatform = (statusResult as { hostPlatform?: unknown } | null)?.hostPlatform diff --git a/mobile/src/transport/settings-read-operations.test.ts b/mobile/src/transport/settings-read-operations.test.ts index 07f4ff771d4..87243dee245 100644 --- a/mobile/src/transport/settings-read-operations.test.ts +++ b/mobile/src/transport/settings-read-operations.test.ts @@ -217,3 +217,27 @@ describe('new-tab settlement barriers', () => { expect(() => readSettings()).toThrow(TypeError) }) }) + +describe('the bound descriptor', () => { + // Eleven call sites pass `interpret` detached from its descriptor, five of them + // settlePreviewSend's second argument in files/mobile-file-preview-request.ts: + // filePreviewTextRead, filePreviewImageRead, terminalArtifactTextRead, + // terminalArtifactImageRead and terminalArtifactWrite. + it('interprets the same reply when taken as an unbound reference', async () => { + const settings = { futureField: 'kept' } + const accepted = await settingsRead.request(replyWith(success({ settings }))) + const readSettings = settingsRead.interpret + expect(readSettings(accepted)).toEqual(settingsRead.interpret(accepted)) + expect(readSettings(accepted)).toEqual({ accepted: true, value: settings }) + + const refused = await botOverridesRead.request(replyWith(refusal())) + const readOverrides = botOverridesRead.interpret + expect(readOverrides(refused)).toEqual(botOverridesRead.interpret(refused)) + expect(readOverrides(refused)).toEqual({ accepted: false }) + + // The throwing acceptance family keeps its throw unbound rather than losing it. + const missing = await newTabSettingsRead.request(replyWith(success(null))) + const readNewTab = newTabSettingsRead.interpret + expect(() => readNewTab(missing)()).toThrow(TypeError) + }) +}) diff --git a/mobile/src/transport/unchecked-rpc-reader-boundary.test.ts b/mobile/src/transport/unchecked-rpc-reader-boundary.test.ts index da07d8dd9c1..efdc426cabf 100644 --- a/mobile/src/transport/unchecked-rpc-reader-boundary.test.ts +++ b/mobile/src/transport/unchecked-rpc-reader-boundary.test.ts @@ -130,11 +130,9 @@ describe('unchecked RPC reader boundary', () => { }) it('scans a plausible number of files', () => { - // A broken root or extension filter would make every check below vacuously pass. The file floor - // is safe to hold at a constant; an offender-count floor is not, because the list counts down to - // zero. Main's batch took it from 29 files to 16 and its floor from 20 to 10; this batch reaches - // 8, below that floor. Against the list instead, the check survives every step of the countdown: - // a filter that scanned nothing reports 0 against a list naming 8. + // A broken root or extension filter would make every check below vacuously pass. The list has + // reached zero, so the equality now asserts "no unchecked reader ships" — which a scan of + // nothing would also satisfy. The file floor is what rules that out, and it stays a constant. expect(scanned.length).toBeGreaterThan(400) expect(observed.size).toBe(inventory.length) }) diff --git a/mobile/src/transport/unchecked-rpc-reader-inventory.ts b/mobile/src/transport/unchecked-rpc-reader-inventory.ts index edcc1417694..44ff9878ab4 100644 --- a/mobile/src/transport/unchecked-rpc-reader-inventory.ts +++ b/mobile/src/transport/unchecked-rpc-reader-inventory.ts @@ -8,20 +8,11 @@ * somewhere downstream — a property read on null, a `.map` on a string, a rendered `undefined` — * with nothing naming the reply as the cause. * - * The count is per file and is a ceiling, not a target: unchecked-rpc-reader-boundary.test.ts fails - * on a file that is not listed, on a listed file that no longer has one, and on a listed file whose - * count went up. Replacing a reader with `rpcResultVariant(variant, schema)` lowers its line; the - * list only shrinks. - * - * A merge is the one case where a line goes up without a migration undoing itself: main can land an - * operation the branch never saw. Raise the line then, and name the PR that brought it, so the next - * reader can tell an import from a regression. Of the three #20954 brought, - * `native-chat-session-page` is still here; `notification-stream-closed` and - * `terminal-buffer-cleared` were converted by the notifications/terminal batch. - * - * A file leaves the list by deletion, not by reaching zero: an entry asserts the file still holds - * at least one unchecked reader, so a `readers: 0` line is itself a failure. Migrating a domain - * therefore removes its files outright. + * The list is empty. Step 7 converted the last domains it named, so the countdown is over and the + * ratchet has flipped direction: unchecked-rpc-reader-boundary.test.ts now fails on the first + * unchecked reader anywhere under `app/` or `src/`, and nothing may be added back. That includes a + * merge bringing an operation this branch never saw — convert it with + * `rpcResultVariant(variant, schema)` in the merge rather than reopening a line here. * * Two holes this list does not close, both deliberate: * - A hand-written reader that returns `{ compatible: true, ... }` without going through those @@ -38,21 +29,7 @@ export type UncheckedRpcReaderEntry = { /** * Files holding at least one unchecked reader, grouped by the feature area that owns them. * - * The reason is shared by every line and is stated once here instead of 37 times: the reply has no - * schema, so the operation declares what the payload is by assertion. Writing one schema per - * consumed member — required exactly where the consumer reads it unguarded, optional everywhere - * else, never `.strict()` — turns the assertion into a check and deletes the line. + * Empty, and the entry shape outlives it: the boundary test measures the scan against this list, so + * an empty list is what makes "no unchecked reader ships" an assertion rather than a claim. */ -export const UNCHECKED_RPC_READERS: readonly UncheckedRpcReaderEntry[] = [ - // agent-history - { file: 'src/agent-history/mobile-agent-history-operations.ts', readers: 6 }, - // dictation - { file: 'src/dictation/mobile-dictation-operations.ts', readers: 8 }, - // files - { file: 'src/files/mobile-file-explorer-operations.ts', readers: 2 }, - { file: 'src/files/mobile-file-ownership-operations.ts', readers: 2 }, - { file: 'src/files/mobile-file-preview-operations.ts', readers: 6 }, - { file: 'src/files/mobile-file-tab-doc-operations.ts', readers: 3 }, - // host-screen - { file: 'src/host-screen/host-screen-operations.ts', readers: 8 } -] +export const UNCHECKED_RPC_READERS: readonly UncheckedRpcReaderEntry[] = [] diff --git a/mobile/src/worktree/worktree-host-context-labels.ts b/mobile/src/worktree/worktree-host-context-labels.ts index de33c62ca5d..406e6d8ce03 100644 --- a/mobile/src/worktree/worktree-host-context-labels.ts +++ b/mobile/src/worktree/worktree-host-context-labels.ts @@ -10,7 +10,6 @@ export { buildHostLabelById, getHostContextLabel } from '../../../src/shared/worktree/host-context-labels' -import type { RepoSummary } from './host-worktree-rpc-types' import type { Worktree } from './workspace-list-types' export type HostLabelSources = { @@ -23,7 +22,7 @@ export type HostLabelSources = { } export function buildRepoHostIdByRepoId( - repos: readonly Pick[] + repos: readonly { id: string; connectionId?: string | null; executionHostId?: string | null }[] ): Map { return new Map(repos.map((repo) => [repo.id, getRepoExecutionHostId(repo)])) } diff --git a/src/shared/execution-host.ts b/src/shared/execution-host.ts index bbe55aea1a0..7c9f3857d07 100644 --- a/src/shared/execution-host.ts +++ b/src/shared/execution-host.ts @@ -155,9 +155,12 @@ export function normalizeExecutionHostOrder( return normalized ?? [] } -export function getRepoExecutionHostId( - repo: Pick -): ExecutionHostId { +// Why the loose member types: a reply reader hands these through as the strings they are on the +// wire, and this function is already the thing that decides what an unparseable spelling means. +export function getRepoExecutionHostId(repo: { + connectionId?: string | null + executionHostId?: string | null +}): ExecutionHostId { const executionHostId = normalizeExecutionHostId(repo.executionHostId) if (executionHostId) { return executionHostId diff --git a/src/shared/zod-salvage.ts b/src/shared/zod-salvage.ts index 6a05550c45a..ccbc9503403 100644 --- a/src/shared/zod-salvage.ts +++ b/src/shared/zod-salvage.ts @@ -93,6 +93,18 @@ export function openEnum fallback)) } +/** + * The arms of a closed enum, spelled as a coverage record over the host's own union so tsc holds + * the schema to that union both ways: an arm the host adds is a missing property here, one it drops + * is an excess property. Call it with the host union as the explicit type argument, or the record + * only pins itself. It has to sit in the schema module, not its test: mobile's tsc excludes test + * files, so a `Record` there checks nothing. + */ +export function hostUnionArms(coverage: Readonly>): readonly U[] { + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the mapped parameter type makes every key exactly a U; Object.keys only loses that at the type level. + return Object.keys(coverage) as U[] +} + /** Array that drops the elements it cannot parse instead of failing. * Absence stays fatal on its own: both containers issue on `undefined` and, being bare transforms, * set neither optin nor optout, and zod only swallows an absent key's issues when a field is both. From 0e7948fa6dca7aec99dbce94d3472c1370b95799 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 16:45:25 -0400 Subject: [PATCH 021/168] feat(relay): pace the drain send during a same-cap cell roll (#21284) * feat(relay): pace the drain send during a same-cap cell roll A same-cap roll drains a cell with graceMs 0, which sends `drain` to all ~800 controls in one pass. Every desktop re-dials on receipt regardless of graceMs, so the whole cell reconnects inside a second. On 2026-09-16 that stampede hit a Cloud SQL stall: attaches timed out, each leaving 10 minutes of late-arrival debt on connection headroom, and placement answered relay_capacity_exhausted fleet-wide for ~13 minutes. Spreading the sends spreads the re-dials. `HostSessionRegistry.drain` takes an optional pacing window and schedules each session's send evenly across it; admission is fenced for every session up front, and each host keeps its own full grace after its own send. /v1/admin/drain accepts `paceWindowMs` (<= 5 min) and echoes what it applied. The same-cap job asks for 120 s, and the drain-completion wait grew by the same amount. A cell still on an older image rejects the field, so the deploy script falls back to an unpaced drain rather than failing the roll. * fix(relay): scope the drain fence to the hosts already told Review of the paced drain found two problems, both from treating "this cell is draining" as one instant when pacing makes it a window. Timers: the sends queued by a paced drain were neither cleared when a later drain superseded them nor unref'd. A SIGTERM mid-window left up to 800 no-op timers holding the event loop open until systemd escalated to SIGKILL. Drain timers are now tracked, cleared on the next drain, and unref'd, so a retry re-arms a session's teardown instead of stacking a second one. Phones: the client fence read the global draining flag, so every phone was refused for the whole window even though its own host had not been told yet and was still serving. The director keeps pointing phones at this cell until their host moves, so they would have looped for up to two minutes. A session is now fenced when its drain is sent, not when the drain starts, and the client paths key off that. New control connections and re-attaches stay fenced globally: nothing new should land on a cell that is going away. --- ...d-deploy-relay-production-same-cap-job.yml | 9 +- cloud/apps/relay/src/app.ts | 16 ++- .../src/host-session-client-accept.test.ts | 70 +++++++++ .../relay/src/host-session-registry.test.ts | 133 +++++++++++++++++- cloud/apps/relay/src/host-session-registry.ts | 59 ++++++-- .../relay/src/regional-host-drain-app.test.ts | 72 ++++++++++ cloud/apps/relay/src/relay-server.ts | 2 +- ...epare-relay-production-capacity-canary.mjs | 59 ++++++-- ...-relay-production-capacity-canary.test.mjs | 74 +++++++++- .../relay-same-cap-script-census.test.mjs | 11 +- 10 files changed, 462 insertions(+), 43 deletions(-) diff --git a/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml b/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml index fa2d55d3d40..f6a577d2b42 100644 --- a/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml +++ b/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml @@ -57,6 +57,9 @@ jobs: GATE_OVERRIDE_REASON: ${{ inputs.gate-override-reason }} GATE_OVERRIDE_CONFIRMATION: ${{ inputs.gate-override-confirmation }} OUTPUT_DIRECTORY: ${{ github.workspace }}/relay-monitor-evidence + # ~800 controls over 2 min is ~7 re-dials/s per cell, well under the director's + # 5 x 80 in-flight assign cap. A cell on an older image ignores it and drains at once. + DRAIN_PACE_WINDOW_MS: '120000' steps: - name: Require exact reusable-workflow configuration working-directory: . @@ -479,14 +482,16 @@ jobs: echo "SELECTOR_GENERATION_AFTER_ISOLATE=${ISOLATE_GENERATION}" >> "${GITHUB_ENV}" node dev/scripts/prepare-relay-production-capacity-canary.mjs \ --director-origin "${DIRECTOR_ORIGIN}" --cell-origin "${CELL_ORIGIN}" \ - --cell-id "${TARGET_CELL_ID}" --approved-cells same-cap --mode drain + --cell-id "${TARGET_CELL_ID}" --approved-cells same-cap --mode drain \ + --pace-window-ms "${DRAIN_PACE_WINDOW_MS}" + # The wait has to outlast the pacing window as well as the leases it waits on. node dev/scripts/verify-relay-capacity-transition.mjs \ --director-origin "${DIRECTOR_ORIGIN}" --cell-origin "${CELL_ORIGIN}" \ --cell-id "${TARGET_CELL_ID}" --hard-cap "${EXPECTED_HARD_CAP}" \ --unobserved-bound "${EXPECTED_UNOBSERVED_BOUND}" \ --heartbeat either --admission migration-only --draining required \ --activity restart-safe --expected-image-digests "${CURRENT_IMAGE_DIGEST}" \ - --timeout-ms 900000 + --timeout-ms 1020000 - id: capacity-auth if: ${{ inputs.mode != 'verify' }} diff --git a/cloud/apps/relay/src/app.ts b/cloud/apps/relay/src/app.ts index d60e2fd264c..978e544f5ff 100644 --- a/cloud/apps/relay/src/app.ts +++ b/cloud/apps/relay/src/app.ts @@ -58,6 +58,8 @@ const RelayCellConnectionHardCapSchema = z.custom( const ASSIGNMENT_REJECTION_LOG_WINDOW_MS = 10_000 const REGION_CATALOG_CACHE_MS = 30_000 +// A drain that outlives the roll step it belongs to is an outage, not a pacing win. +const DRAIN_PACE_WINDOW_MAX_MS = 5 * 60 * 1_000 type AdmissionRejectionLogEntry = { route: 'assign' | 'resolve' @@ -72,7 +74,7 @@ export function createRelayApp( operations: { store: RelayCredentialStore assignments: RelayAssignmentStore - drain: (graceMs: number) => void + drain: (graceMs: number, options?: { paceWindowMs?: number }) => void idleRehome?: (input: IdleRegionalRehomeRequest & { cohortPercent: number directorSafety: RegionalRehomeSafetySnapshot @@ -488,12 +490,18 @@ export function createRelayApp( return context.json({ error: 'invalid_token' }, 401) } const body = z - .object({ v: z.literal(1), graceMs: z.number().int().nonnegative().max(60 * 60 * 1000) }) + .object({ + v: z.literal(1), + graceMs: z.number().int().nonnegative().max(60 * 60 * 1000), + // Spreads the drain sends, and so the re-dials, over this window. + paceWindowMs: z.number().int().nonnegative().max(DRAIN_PACE_WINDOW_MAX_MS).optional() + }) .strict() .safeParse(await context.req.json().catch(() => null)) if (!body.success) return context.json({ error: 'invalid_request' }, 400) - operations.drain(body.data.graceMs) - return context.json({ ok: true }) + const paceWindowMs = body.data.paceWindowMs ?? 0 + operations.drain(body.data.graceMs, { paceWindowMs }) + return context.json({ ok: true, paceWindowMs }) }) app.post('/v1/admin/host-idle-rehome', async (context) => { if (config.role !== 'cell' || !operations.idleRehome) { diff --git a/cloud/apps/relay/src/host-session-client-accept.test.ts b/cloud/apps/relay/src/host-session-client-accept.test.ts index 0b7febaa7b4..7806e8d25b3 100644 --- a/cloud/apps/relay/src/host-session-client-accept.test.ts +++ b/cloud/apps/relay/src/host-session-client-accept.test.ts @@ -729,3 +729,73 @@ describe('control lease jitter', () => { vi.advanceTimersByTime(0) }) }) + +describe('paced drain and the phones of a host not yet told', () => { + beforeEach(() => vi.useFakeTimers()) + afterEach(() => { + vi.clearAllTimers() + vi.useRealTimers() + }) + + const laterHostId = 'qrstuvwxyz012345' + const laterIdentity = { ...identity, sub: 'user-2', relayHostId: laterHostId } + + async function twoHostCell(): Promise<{ + h: ReturnType + told: FakeSocket + untold: FakeSocket + }> { + const h = harness() + const told = await activeHost(h) + const untold = new FakeSocket() + await h.activate(untold as unknown as WebSocket, laterIdentity, null, 1, false, 1, '1.4.197') + // Both hosts now dial in, so the credential mocks have to answer for either. + h.store.resolveResume.mockImplementation(async (hostId: string) => ({ + userId: hostId === laterHostId ? laterIdentity.sub : identity.sub + })) + h.store.reserveCredential.mockImplementation(async (hostId: string) => ({ + ...reservation, + userId: hostId === laterHostId ? laterIdentity.sub : identity.sub, + relayHostId: hostId + })) + return { h, told, untold } + } + + async function dial(h: ReturnType, hostId: string): Promise { + const client = new FakeSocket() + await h.registry.acceptClient(client as unknown as WebSocket, hostId, 'credential') + return client + } + + it('serves a host whose drain has not been sent and refuses one whose has', async () => { + const { h, told, untold } = await twoHostCell() + h.registry.drain(0, { paceWindowMs: 40_000 }) + + const refused = await dial(h, identity.relayHostId) + expect(refused.close).toHaveBeenCalledWith(RELAY_CLOSE_CODE.DRAINING, expect.any(String)) + expect(told.send).not.toHaveBeenCalledWith(expect.stringContaining('conn-open')) + + const served = await dial(h, laterHostId) + expect(served.close).not.toHaveBeenCalled() + expect(untold.send).toHaveBeenCalledWith(expect.stringContaining('conn-open')) + }) + + it('refuses that host\'s phones as soon as its own drain is sent', async () => { + const { h, untold } = await twoHostCell() + h.registry.drain(0, { paceWindowMs: 40_000 }) + await vi.advanceTimersByTimeAsync(40_000) + expect(untold.send).toHaveBeenCalledWith(expect.stringContaining('"type":"drain"')) + + const refused = await dial(h, laterHostId) + expect(refused.close).toHaveBeenCalledWith(RELAY_CLOSE_CODE.DRAINING, expect.any(String)) + }) + + it('keeps an unpaced drain refusing every phone at once', async () => { + const { h } = await twoHostCell() + h.registry.drain(0) + for (const hostId of [identity.relayHostId, laterHostId]) { + const refused = await dial(h, hostId) + expect(refused.close).toHaveBeenCalledWith(RELAY_CLOSE_CODE.DRAINING, expect.any(String)) + } + }) +}) diff --git a/cloud/apps/relay/src/host-session-registry.test.ts b/cloud/apps/relay/src/host-session-registry.test.ts index 7c50285454c..7fcff6a79f4 100644 --- a/cloud/apps/relay/src/host-session-registry.test.ts +++ b/cloud/apps/relay/src/host-session-registry.test.ts @@ -336,15 +336,15 @@ describe('host session cleanup races', () => { session.activeSplices.set('conn-a', () => session.activeSplices.delete('conn-a')) // POST /v1/admin/drain has no idempotency guard, and SIGTERM then SIGINT both - // reach drain(), so a second teardown can be scheduled for the same session. + // reach drain(), so a retry re-sends to every session. It must re-arm the pending + // teardown rather than stack a second one: across a paced cell that is 800 orphaned + // timers per retry, each one holding the loop open for the rest of the window. registry.drain(0) const scheduled = vi.getTimerCount() registry.drain(0) - // Pin the premise: if drain ever gains an idempotency guard, the retry schedules no - // second teardown and the assertion below stops defending the write-once snapshot - // while still passing. Compare against the count before the retry rather than an - // absolute, since the session's heartbeat interval is also pending. - expect(vi.getTimerCount()).toBe(scheduled + 1) + // Compare against the count before the retry rather than an absolute, since the + // session's heartbeat interval is also pending. + expect(vi.getTimerCount()).toBe(scheduled) vi.advanceTimersByTime(1) // Asserting registry state, not the log line: FakeSocket closes synchronously, so @@ -1696,3 +1696,124 @@ describe('host data attach owner lookup', () => { expect(h.owner.activeConnIds.size).toBe(0) }) }) + +describe('paced drain', () => { + beforeEach(() => vi.useFakeTimers()) + afterEach(() => { + vi.clearAllTimers() + vi.useRealTimers() + }) + + async function connectHosts(count: number): Promise<{ + registry: HostSessionRegistry + sockets: FakeSocket[] + }> { + const activateControl = vi + .fn() + .mockResolvedValue('control:production-gce-c3:1') + const { registry, activate } = createRegistry(activateControl) + const sockets: FakeSocket[] = [] + for (let index = 0; index < count; index += 1) { + const socket = new FakeSocket() + sockets.push(socket) + await activate( + socket as unknown as WebSocket, + { ...identity, sub: `user-${index}` }, + null, + 1, + false, + 1 + ) + socket.send.mockClear() + } + return { registry, sockets } + } + + function drainsSent(sockets: FakeSocket[]): number { + return sockets.filter((socket) => + socket.send.mock.calls.some(([payload]) => String(payload).includes('"type":"drain"')) + ).length + } + + it('sends every drain at once when no window is given', async () => { + const { registry, sockets } = await connectHosts(4) + registry.drain(0) + expect(drainsSent(sockets)).toBe(4) + }) + + // Windows here stay under the 75s control-silence watchdog, which would otherwise close + // a test socket that never heartbeats before its paced send is due. + it('spreads the sends evenly across the window', async () => { + const { registry, sockets } = await connectHosts(5) + registry.drain(0, { paceWindowMs: 40_000 }) + // The first host is sent synchronously; the last lands on the window's closing edge. + expect(drainsSent(sockets)).toBe(1) + await vi.advanceTimersByTimeAsync(10_000) + expect(drainsSent(sockets)).toBe(2) + await vi.advanceTimersByTimeAsync(20_000) + expect(drainsSent(sockets)).toBe(4) + await vi.advanceTimersByTimeAsync(10_000) + expect(drainsSent(sockets)).toBe(5) + }) + + it('fences admission for every session before the first paced send lands', async () => { + const { registry, sockets } = await connectHosts(3) + registry.drain(0, { paceWindowMs: 40_000 }) + expect(registry.isDraining()).toBe(true) + // A host whose drain has not been sent yet must already be non-authoritative. + const socket = new FakeSocket() + registry.acceptControl(socket as unknown as WebSocket, { ...identity, sub: 'user-late' }) + expect(socket.close).toHaveBeenCalledWith(RELAY_CLOSE_CODE.DRAINING, 'relay draining') + expect(drainsSent(sockets)).toBe(1) + }) + + it('gives each host its own grace after its own send, not after the call', async () => { + const { registry, sockets } = await connectHosts(2) + registry.drain(10_000, { paceWindowMs: 40_000 }) + await vi.advanceTimersByTimeAsync(10_000) + expect(sockets[0]!.readyState).toBe(sockets[0]!.CLOSED) + expect(sockets[1]!.readyState).toBe(sockets[1]!.OPEN) + // Its own send at 40s plus its own 10s grace, not 10s from the drain call. + await vi.advanceTimersByTimeAsync(39_999) + expect(sockets[1]!.readyState).toBe(sockets[1]!.OPEN) + await vi.advanceTimersByTimeAsync(10_001) + expect(sockets[1]!.readyState).toBe(sockets[1]!.CLOSED) + }) + + it('leaves no timer behind once an emergency drain cuts a window short', async () => { + const { registry } = await connectHosts(4) + registry.drain(0, { paceWindowMs: 40_000 }) + registry.drain(0) + await vi.advanceTimersByTimeAsync(0) + // Every session is closed, so anything still pending is an orphan of the cut window. + expect(vi.getTimerCount()).toBe(0) + }) + + it('keeps the first teardown snapshot when a regional drain fires before the fleet one', async () => { + const { registry, sockets } = await connectHosts(1) + const session = registry.get({ userId: 'user-0', relayHostId: identity.relayHostId })! + session.activeSplices.set('conn-a', () => session.activeSplices.delete('conn-a')) + registry.drainHost({ + attemptId: 'attempt', + userId: 'user-0', + relayHostId: identity.relayHostId, + sourceAssignmentEpoch: 1, + graceMs: 0 + }) + registry.drain(10) + await vi.advanceTimersByTimeAsync(11) + expect(session.closingCounts).toEqual({ splices: 1, pending: 0 }) + expect(sockets[0]!.readyState).toBe(sockets[0]!.CLOSED) + }) + + it('lets an emergency drain supersede the sends still queued by a paced one', async () => { + const { registry, sockets } = await connectHosts(4) + registry.drain(0, { paceWindowMs: 40_000 }) + expect(drainsSent(sockets)).toBe(1) + registry.drain(0) + expect(drainsSent(sockets)).toBe(4) + const sendsAfterEmergency = sockets.map((socket) => socket.send.mock.calls.length) + await vi.advanceTimersByTimeAsync(40_000) + expect(sockets.map((socket) => socket.send.mock.calls.length)).toEqual(sendsAfterEmergency) + }) +}) diff --git a/cloud/apps/relay/src/host-session-registry.ts b/cloud/apps/relay/src/host-session-registry.ts index 0d0cf4a7940..35e3935fb7d 100644 --- a/cloud/apps/relay/src/host-session-registry.ts +++ b/cloud/apps/relay/src/host-session-registry.ts @@ -179,6 +179,10 @@ export class HostSessionRegistry { private readonly hostCloseReasons = new HostCloseReasonMemory(() => this.now()) private readonly hostCapabilities = new WeakMap>() private draining = false + private readonly drainTimers = new Set>() + // Hosts whose drain has been sent. Paced sends land minutes apart, so "this cell is + // draining" is not the same question as "this host has been told to leave". + private readonly drainSentHosts = new Set() private readonly idleWork = new Map() private readonly idleAttempts = new Map< @@ -330,7 +334,10 @@ export class HostSessionRegistry { credential: string, capacityReservation?: PendingHostDataReservation ): Promise { - if (this.draining) { + // Not `this.draining`: a paced drain tells hosts minutes apart, and the director keeps + // pointing phones here until their own host has moved. Refusing them for the whole + // window would turn a 2 min drain into a 2 min outage for hosts not yet told. + if (this.drainSentHosts.has(hostId)) { capacityReservation?.release() this.rejectClient(socket, RELAY_CLOSE_CODE.DRAINING) return @@ -450,7 +457,7 @@ export class HostSessionRegistry { } // Admission may have crossed a drain or control replacement while persisting activity. if ( - this.draining || + this.drainSentHosts.has(hostId) || this.sessions.get(sessionKey) !== session || session.state !== 'active' || session.socket !== admittingSocket || @@ -592,7 +599,7 @@ export class HostSessionRegistry { } // Already admitted attachments may finish a regional drain, but never a retired generation. if ( - this.draining || + this.drainSentHosts.has(identity.relayHostId) || this.sessions.get(this.key(identity.userId, identity.relayHostId)) !== session || this.get(identity)?.state === 'closed' || !session.activeConnIds.has(connId) || @@ -846,17 +853,49 @@ export class HostSessionRegistry { return { controls, splices, pendingSplices } } - drain(graceMs: number): void { + drain(graceMs: number, options: { paceWindowMs?: number } = {}): void { this.draining = true - for (const session of this.sessions.values()) { - if (session.state === 'closed') continue - session.authorityRevision += 1 - session.state = 'drain-only' - if (session.socket) send(session.socket, 'drain', { graceMs, recovery: 'resolve-director' }) - setTimeout(() => this.closeDrainedSession(session), graceMs) + // A later drain (an emergency one, or shutdown) owns every session again, so nothing + // queued by an earlier paced drain may still fire: it would re-send and, worse, keep + // the event loop alive for the rest of a window the operator just cut short. + for (const timer of this.drainTimers) clearTimeout(timer) + this.drainTimers.clear() + const paceWindowMs = Math.max(0, Math.trunc(options.paceWindowMs ?? 0)) + const targets = [...this.sessions.values()].filter((session) => session.state !== 'closed') + // The desktop re-dials the director as soon as it reads `drain`, whatever graceMs says, + // so spreading the send is the only thing that spreads the reconnect load. + const step = paceWindowMs > 0 && targets.length > 1 ? paceWindowMs / (targets.length - 1) : 0 + for (const [index, session] of targets.entries()) { + const delay = Math.round(step * index) + if (delay === 0) { + this.sendDrain(session, graceMs) + continue + } + this.scheduleDrainTimer(delay, () => this.sendDrain(session, graceMs)) } } + // A session is only fenced when it is told, not when the drain starts: until its send + // lands it is an ordinary live host, and its phones have to keep being able to reach it. + private sendDrain(session: HostSession, graceMs: number): void { + if (session.state === 'closed') return + session.authorityRevision += 1 + session.state = 'drain-only' + this.drainSentHosts.add(session.relayHostId) + if (session.socket) send(session.socket, 'drain', { graceMs, recovery: 'resolve-director' }) + this.scheduleDrainTimer(graceMs, () => this.closeDrainedSession(session)) + } + + // Unref'd so a drain in flight never holds the process open past its own work. + private scheduleDrainTimer(delayMs: number, run: () => void): void { + const timer: ReturnType = setTimeout(() => { + this.drainTimers.delete(timer) + run() + }, delayMs) + timer.unref?.() + this.drainTimers.add(timer) + } + drainHost(input: { attemptId: string userId: string diff --git a/cloud/apps/relay/src/regional-host-drain-app.test.ts b/cloud/apps/relay/src/regional-host-drain-app.test.ts index cf7e3798155..38ebdd3f709 100644 --- a/cloud/apps/relay/src/regional-host-drain-app.test.ts +++ b/cloud/apps/relay/src/regional-host-drain-app.test.ts @@ -697,6 +697,78 @@ async function postPath( }) } +describe('cell drain endpoint pacing', () => { + function appWithDrain(): { + app: ReturnType + drain: ReturnType + } { + const drain = vi.fn() + const app = createRelayApp(config(), { + store: {} as never, + assignments: {} as never, + drain, + cellIncarnation, + ready: vi.fn(async () => true) + } as Parameters[1]) + return { app, drain } + } + + it('drains everything at once when the caller asks for no pacing', async () => { + const { app, drain } = appWithDrain() + const response = await postPath(app, '/v1/admin/drain', 'deploy-token', { v: 1, graceMs: 0 }) + expect(response.status).toBe(200) + expect(await response.json()).toEqual({ ok: true, paceWindowMs: 0 }) + expect(drain).toHaveBeenCalledWith(0, { paceWindowMs: 0 }) + }) + + it('passes the requested window through and echoes what it accepted', async () => { + const { app, drain } = appWithDrain() + const response = await postPath(app, '/v1/admin/drain', 'deploy-token', { + v: 1, + graceMs: 0, + paceWindowMs: 120_000 + }) + expect(response.status).toBe(200) + expect(await response.json()).toEqual({ ok: true, paceWindowMs: 120_000 }) + expect(drain).toHaveBeenCalledWith(0, { paceWindowMs: 120_000 }) + }) + + it('refuses a window that is negative, fractional, or past the cap', async () => { + for (const paceWindowMs of [-1, 1.5, 300_001]) { + const { app, drain } = appWithDrain() + const response = await postPath(app, '/v1/admin/drain', 'deploy-token', { + v: 1, + graceMs: 0, + paceWindowMs + }) + expect(response.status).toBe(400) + expect(drain).not.toHaveBeenCalled() + } + }) + + it('accepts the cap itself', async () => { + const { app, drain } = appWithDrain() + const response = await postPath(app, '/v1/admin/drain', 'deploy-token', { + v: 1, + graceMs: 0, + paceWindowMs: 300_000 + }) + expect(response.status).toBe(200) + expect(drain).toHaveBeenCalledWith(0, { paceWindowMs: 300_000 }) + }) + + it('still rejects an unauthenticated pacing request', async () => { + const { app, drain } = appWithDrain() + const response = await postPath(app, '/v1/admin/drain', 'wrong-token', { + v: 1, + graceMs: 0, + paceWindowMs: 120_000 + }) + expect(response.status).toBe(401) + expect(drain).not.toHaveBeenCalled() + }) +}) + function config(overrides: Partial = {}): RelayConfig { return { port: 8080, diff --git a/cloud/apps/relay/src/relay-server.ts b/cloud/apps/relay/src/relay-server.ts index 50077b41116..3a0668bbcef 100644 --- a/cloud/apps/relay/src/relay-server.ts +++ b/cloud/apps/relay/src/relay-server.ts @@ -136,7 +136,7 @@ export function createRelayServer( const app = createRelayApp(config, { store, assignments, - drain: (graceMs) => sessions.drain(graceMs), + drain: (graceMs, options) => sessions.drain(graceMs, options ?? {}), drainHost: (input) => sessions.drainHost(input), idleRehome: (input) => { const now = (options.now ?? Date.now)() diff --git a/cloud/dev/scripts/prepare-relay-production-capacity-canary.mjs b/cloud/dev/scripts/prepare-relay-production-capacity-canary.mjs index 7967d164e4b..9787dbdd62a 100644 --- a/cloud/dev/scripts/prepare-relay-production-capacity-canary.mjs +++ b/cloud/dev/scripts/prepare-relay-production-capacity-canary.mjs @@ -35,6 +35,9 @@ function cellOrigin(cellId) { // The same-cap roll covers the Asia cells the US-only capacity rollout never touches. const APPROVED_CELL_LISTS = { 'same-cap': SAME_CAP_CELLS } +// Matches the cell's own cap on /v1/admin/drain. +const MAX_PACE_WINDOW_MS = 5 * 60 * 1_000 + export function parseProductionCapacityCellArguments(argv) { const values = {} for (let index = 0; index < argv.length; index += 2) { @@ -64,11 +67,22 @@ export function parseProductionCapacityCellArguments(argv) { ) { throw new Error('production capacity target origin is not exact') } + const paceWindowMs = values['pace-window-ms'] === undefined + ? 0 + : Number(values['pace-window-ms']) + if ( + !Number.isSafeInteger(paceWindowMs) || + paceWindowMs < 0 || + paceWindowMs > MAX_PACE_WINDOW_MS + ) { + throw new Error('--pace-window-ms must be an integer between 0 and 300000') + } return { directorOrigin: DIRECTOR_ORIGIN, cellOrigin: expectedCellOrigin, cellId, - mode: values.mode + mode: values.mode, + paceWindowMs } } @@ -82,24 +96,39 @@ export async function prepareProductionCapacityCell(config, overrides = {}) { const fetchImpl = overrides.fetch ?? fetch const token = overrides.token ?? process.env.ORCA_RELAY_ADMIN_ID_TOKEN if (!token || token.length > 8_192) throw new Error('admin identity token is unavailable') - const postAt = async (origin, path, body) => - await responseJson( - await fetchAdminOnceMore( - fetchImpl, - `${origin}${path}`, - { - method: 'POST', - headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' }, - body: JSON.stringify(body) - }, - { wait: overrides.wait } - ), - path + const postRaw = async (origin, path, body) => + await fetchAdminOnceMore( + fetchImpl, + `${origin}${path}`, + { + method: 'POST', + headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' }, + body: JSON.stringify(body) + }, + { wait: overrides.wait } ) + const postAt = async (origin, path, body) => + await responseJson(await postRaw(origin, path, body), path) const post = async (path, body) => await postAt(config.directorOrigin, path, body) if (config.mode === 'drain') { + const paceWindowMs = config.paceWindowMs ?? 0 + if (paceWindowMs > 0) { + const paced = await postRaw(config.cellOrigin, '/v1/admin/drain', { + v: 1, + graceMs: 0, + paceWindowMs + }) + if (paced.ok) { + await paced.json().catch(() => ({})) + return { changed: false, drained: true, paceWindowMs } + } + // A cell still on an image without paced drain rejects the unknown field outright. + // An unpaced drain is the behaviour that cell already has, so fall back to it. + if (paced.status !== 400) throw new Error(`/v1/admin/drain returned ${paced.status}`) + await paced.json().catch(() => ({})) + } await postAt(config.cellOrigin, '/v1/admin/drain', { v: 1, graceMs: 0 }) - return { changed: false, drained: true } + return { changed: false, drained: true, paceWindowMs: 0 } } const before = await inspectAdmissionSelector(post) const state = selectorCellState(before.selector, config.cellId) diff --git a/cloud/dev/scripts/prepare-relay-production-capacity-canary.test.mjs b/cloud/dev/scripts/prepare-relay-production-capacity-canary.test.mjs index 274a60d2198..c566229b678 100644 --- a/cloud/dev/scripts/prepare-relay-production-capacity-canary.test.mjs +++ b/cloud/dev/scripts/prepare-relay-production-capacity-canary.test.mjs @@ -90,7 +90,8 @@ describe('production Relay capacity cell admission', () => { directorOrigin: 'https://relay.onorca.dev', cellOrigin: 'https://c7.relay.onorca.dev', cellId: 'production-gce-c7', - mode: 'isolate' + mode: 'isolate', + paceWindowMs: 0 }) assert.throws(() => parseProductionCapacityCellArguments([ '--director-origin', 'https://relay.onorca.dev', @@ -125,7 +126,8 @@ describe('production Relay capacity cell admission', () => { directorOrigin: 'https://relay.onorca.dev', cellOrigin: `https://${hostname}.relay.onorca.dev`, cellId, - mode: 'isolate' + mode: 'isolate', + paceWindowMs: 0 }) } for (const cellId of ['production-gce-c17', 'production-gce-c18', 'production-gce-c30']) { @@ -168,13 +170,77 @@ describe('production Relay capacity cell admission', () => { { ...config, mode: 'drain' }, { fetch: fake.fetch, token: 'token' } ) - assert.deepEqual(result, { changed: false, drained: true }) + assert.deepEqual(result, { changed: false, drained: true, paceWindowMs: 0 }) assert.deepEqual(fake.calls, [{ path: '/v1/admin/drain', body: { v: 1, graceMs: 0 } }]) }) + it('paces the drain send when the roll asks for a window', async () => { + const fake = canaryFetch() + const result = await prepareProductionCapacityCell( + { ...config, mode: 'drain', paceWindowMs: 120_000 }, + { fetch: fake.fetch, token: 'token' } + ) + assert.deepEqual(result, { changed: false, drained: true, paceWindowMs: 120_000 }) + assert.deepEqual(fake.calls, [{ + path: '/v1/admin/drain', + body: { v: 1, graceMs: 0, paceWindowMs: 120_000 } + }]) + }) + + it('drains unpaced when the cell image rejects the pacing field', async () => { + const bodies = [] + const result = await prepareProductionCapacityCell( + { ...config, mode: 'drain', paceWindowMs: 120_000 }, + { + token: 'token', + wait: async () => {}, + fetch: async (url, init) => { + assert.equal(new URL(url).pathname, '/v1/admin/drain') + const body = JSON.parse(init.body) + bodies.push(body) + if (body.paceWindowMs !== undefined) return response({ error: 'invalid_request' }, 400) + return response({ v: 1, draining: true }) + } + } + ) + assert.deepEqual(result, { changed: false, drained: true, paceWindowMs: 0 }) + assert.deepEqual(bodies, [ + { v: 1, graceMs: 0, paceWindowMs: 120_000 }, + { v: 1, graceMs: 0 } + ]) + }) + + it('fails a paced drain that the cell rejects for any other reason', async () => { + await assert.rejects( + prepareProductionCapacityCell( + { ...config, mode: 'drain', paceWindowMs: 120_000 }, + { + token: 'token', + wait: async () => {}, + fetch: async () => response({ error: 'invalid_token' }, 401) + } + ), + /returned 401/ + ) + }) + + it('refuses a pacing window that is not a bounded integer', () => { + const argv = (value) => [ + '--director-origin', 'https://relay.onorca.dev', + '--cell-origin', 'https://c26.relay.onorca.dev', + '--cell-id', 'production-gce-c26', + '--mode', 'drain', + '--pace-window-ms', value + ] + for (const value of ['-1', '300001', '1.5', 'soon']) { + assert.throws(() => parseProductionCapacityCellArguments(argv(value)), /pace-window-ms/) + } + assert.equal(parseProductionCapacityCellArguments(argv('300000')).paceWindowMs, 300_000) + }) + it('restores only the selected cell to general admission', async () => { const fake = canaryFetch() await prepareProductionCapacityCell( @@ -228,7 +294,7 @@ describe('production Relay capacity cell admission', () => { } ) assert.equal(calls, 2) - assert.deepEqual(result, { changed: false, drained: true }) + assert.deepEqual(result, { changed: false, drained: true, paceWindowMs: 0 }) }) it('fails when both drain attempts return a transient 503', async () => { diff --git a/cloud/dev/scripts/relay-same-cap-script-census.test.mjs b/cloud/dev/scripts/relay-same-cap-script-census.test.mjs index 770e20b6353..bd49f7600a7 100644 --- a/cloud/dev/scripts/relay-same-cap-script-census.test.mjs +++ b/cloud/dev/scripts/relay-same-cap-script-census.test.mjs @@ -159,7 +159,8 @@ describe('same-cap roll scripts accept every same-cap cell', () => { directorOrigin: 'https://relay.onorca.dev', cellOrigin: `https://${hostname(cellId)}.relay.onorca.dev`, cellId, - mode + mode, + paceWindowMs: 0 }) } } @@ -192,6 +193,14 @@ describe('same-cap roll scripts accept every same-cap cell', () => { } }) + it('paces the drain it sends to the selected cell', () => { + const drain = workflow.split('--mode drain')[1] ?? '' + assert.match(drain.split('\n').slice(0, 2).join(' '), /--pace-window-ms "\$\{DRAIN_PACE_WINDOW_MS\}"/) + assert.match(workflow, /DRAIN_PACE_WINDOW_MS: '120000'/) + // The transition wait has to outlast the pacing window on top of the leases it waits on. + assert.match(workflow, /--activity restart-safe[\s\S]*?--timeout-ms 1020000/) + }) + it('passes this cell\'s rehome protocol and pool on every plan validation the job runs', () => { const invocations = workflow.split('validate-relay-capacity-plan.mjs').slice(1) assert.equal(invocations.length, 2) From 0b1cde0e017bf52fce838a977e54d1ef688bc42b Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 16:53:06 -0400 Subject: [PATCH 022/168] chore(mobile): repin the RPC recording baseline to main after #21269 (#21287) The last step-7 squash orphaned the pin again. Repin to 4a86b2dc56 and re-record: 778 goldens and the manifest move only on the baseline field. With this the unchecked-reader inventory on main is empty. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- .../rpc-foundation/goldens/aivault-history-scan-fulfilled.json | 2 +- .../goldens/aivault-history-scan-unsupported.json | 2 +- .../goldens/aivault-history-scan-worktrees-late.json | 2 +- .../rpc-foundation/goldens/aivault-history-screen-listed.json | 2 +- .../goldens/aivault-history-screen-worktrees.json | 2 +- .../goldens/aivault-resume-launch-create-refused.json | 2 +- .../goldens/aivault-resume-launch-invalid-tab.json | 2 +- mobile/rpc-foundation/goldens/aivault-resume-launch-locked.json | 2 +- mobile/rpc-foundation/goldens/aivault-resume-launch-sent.json | 2 +- .../rpc-foundation/goldens/aivault-resume-prepare-refused.json | 2 +- mobile/rpc-foundation/goldens/aivault-resume-prepare-repin.json | 2 +- .../rpc-foundation/goldens/aivault-resume-prepare-skipped.json | 2 +- .../goldens/aivault-resume-prepare-unavailable.json | 2 +- mobile/rpc-foundation/goldens/b1.json | 2 +- mobile/rpc-foundation/goldens/b2.json | 2 +- mobile/rpc-foundation/goldens/b3.json | 2 +- mobile/rpc-foundation/goldens/browser-dialog-accepted.json | 2 +- mobile/rpc-foundation/goldens/browser-dialog-dismissed.json | 2 +- mobile/rpc-foundation/goldens/browser-keyboard-input.json | 2 +- .../rpc-foundation/goldens/browser-pointer-click-accepted.json | 2 +- .../rpc-foundation/goldens/browser-pointer-click-fallback.json | 2 +- mobile/rpc-foundation/goldens/browser-wheel-scrolled.json | 2 +- .../goldens/clipboard-image-attachment-anonymous.json | 2 +- .../goldens/clipboard-image-attachment-blocked-before-send.json | 2 +- .../goldens/clipboard-image-attachment-cancelled.json | 2 +- .../goldens/clipboard-image-attachment-pasted.json | 2 +- .../goldens/clipboard-image-attachment-upload-refused.json | 2 +- .../goldens/clipboard-image-upload-aborts-on-chunk-failure.json | 2 +- .../rpc-foundation/goldens/clipboard-image-upload-chunked.json | 2 +- .../goldens/clipboard-image-upload-single-frame-fallback.json | 2 +- .../goldens/clipboard-image-upload-start-refused.json | 2 +- mobile/rpc-foundation/goldens/codex-reset-credit-consumed.json | 2 +- mobile/rpc-foundation/goldens/codex-reset-credit-resumed.json | 2 +- mobile/rpc-foundation/goldens/components-codex-capability.json | 2 +- mobile/rpc-foundation/goldens/components-setup-ask.json | 2 +- mobile/rpc-foundation/goldens/components-target-local.json | 2 +- mobile/rpc-foundation/goldens/components-target-ssh.json | 2 +- mobile/rpc-foundation/goldens/diff-review-branch-compare.json | 2 +- mobile/rpc-foundation/goldens/diff-review-branch-file-diff.json | 2 +- .../goldens/diff-review-notes-refused-before-compare.json | 2 +- .../rpc-foundation/goldens/diff-review-refused-file-diff.json | 2 +- mobile/rpc-foundation/goldens/diff-review-snapshot.json | 2 +- .../rpc-foundation/goldens/diff-review-status-unavailable.json | 2 +- .../rpc-foundation/goldens/diff-review-worktree-file-diff.json | 2 +- mobile/rpc-foundation/goldens/file-tap-open-refused.json | 2 +- mobile/rpc-foundation/goldens/file-tap-opens-worktree-file.json | 2 +- .../goldens/file-tap-previews-absolute-artifact.json | 2 +- mobile/rpc-foundation/goldens/file-tap-resolve-miss.json | 2 +- mobile/rpc-foundation/goldens/file-tap-resolve-refused.json | 2 +- .../rpc-foundation/goldens/files-explorer-legacy-fallback.json | 2 +- mobile/rpc-foundation/goldens/files-explorer-readdir.json | 2 +- mobile/rpc-foundation/goldens/files-ownership-local.json | 2 +- mobile/rpc-foundation/goldens/files-ownership-ssh.json | 2 +- .../rpc-foundation/goldens/files-preview-artifact-direct.json | 2 +- .../goldens/files-preview-artifact-image-read.json | 2 +- mobile/rpc-foundation/goldens/files-preview-artifact-image.json | 2 +- mobile/rpc-foundation/goldens/files-preview-grant-refresh.json | 2 +- .../goldens/files-preview-worktree-image-read.json | 2 +- mobile/rpc-foundation/goldens/files-preview-worktree-image.json | 2 +- .../goldens/files-preview-worktree-text-read.json | 2 +- mobile/rpc-foundation/goldens/files-preview-worktree.json | 2 +- mobile/rpc-foundation/goldens/files-save-blind.json | 2 +- mobile/rpc-foundation/goldens/files-save-verified.json | 2 +- mobile/rpc-foundation/goldens/files-tab-doc-shapes.json | 2 +- mobile/rpc-foundation/goldens/home-host-accounts.json | 2 +- mobile/rpc-foundation/goldens/home-host-stats.json | 2 +- mobile/rpc-foundation/goldens/host-view-settings-sync.json | 2 +- .../goldens/host-worktree-actions-pin-open-delete.json | 2 +- mobile/rpc-foundation/goldens/host-worktree-delete-refused.json | 2 +- mobile/rpc-foundation/goldens/host-worktree-refresh-stream.json | 2 +- .../goldens/interruptions-inventory-lifecycle.json | 2 +- .../goldens/interruptions-settings-bot-overrides-fulfilled.json | 2 +- mobile/rpc-foundation/goldens/inventory-lifecycle.json | 2 +- mobile/rpc-foundation/goldens/inventory-repeat-query.json | 2 +- mobile/rpc-foundation/goldens/lifecycle-b3.json | 2 +- .../rpc-foundation/goldens/lifecycle-inventory-lifecycle.json | 2 +- .../goldens/lifecycle-settings-bot-overrides-fulfilled.json | 2 +- .../goldens/lifecycle-settings-task-hydration-fulfilled.json | 2 +- .../goldens/lifecycle-settings-workspace-context-fulfilled.json | 2 +- mobile/rpc-foundation/goldens/linear-select-workspace.json | 2 +- mobile/rpc-foundation/goldens/live-worktree-name-stream.json | 2 +- ...ix-agentsession.structured-create-agentsession.create-1.json | 2 +- ...tsession.structured-create-agentsession.createsupport-1.json | 2 +- ...tsession.structured-launch-agentsession.createsupport-1.json | 2 +- .../goldens/matrix-aivault.history-aivault.listsessions-1.json | 2 +- .../goldens/matrix-aivault.history-screen-platform-status.json | 2 +- .../goldens/matrix-aivault.history-screen-status.get-2.json | 2 +- .../goldens/matrix-aivault.history-screen-worktree.ps-1.json | 2 +- .../goldens/matrix-aivault.history-status.get-1.json | 2 +- ...rix-aivault.resume-launch-session.tabs.createterminal-1.json | 2 +- .../goldens/matrix-aivault.resume-launch-terminal.send-1.json | 2 +- ...vault.resume-preparation-aivault.preparesessionresume-1.json | 2 +- .../goldens/matrix-browser.dialog-browser.dialogaccept-1.json | 2 +- .../matrix-browser.keyboard-browser.keyboardinserttext-1.json | 2 +- .../goldens/matrix-browser.keyboard-browser.keypress-1.json | 2 +- .../matrix-browser.pointer-click-browser.mouseclick-1.json | 2 +- .../matrix-browser.pointer-click-browser.mousedown-1.json | 2 +- .../matrix-browser.pointer-click-browser.mousemove-1.json | 2 +- .../goldens/matrix-browser.pointer-click-browser.mouseup-1.json | 2 +- .../goldens/matrix-browser.wheel-browser.mousemove-1.json | 2 +- .../goldens/matrix-browser.wheel-browser.mousewheel-1.json | 2 +- ...clipboard.image-attachment-clipboard.startimageupload-1.json | 2 +- ...-clipboard.image-upload-clipboard.saveimageastempfile-1.json | 2 +- ...rix-clipboard.image-upload-clipboard.startimageupload-1.json | 2 +- .../matrix-components.codex-reset-capability-status.get-1.json | 2 +- ...s.codex-reset-credit-accounts.consumecodexresetcredit-1.json | 2 +- ...ponents.execution-target-local-preflight.detectagents-1.json | 2 +- ...ponents.execution-target-preflight.detectremoteagents-1.json | 2 +- .../matrix-components.execution-target-ssh.connect-1.json | 2 +- .../matrix-components.execution-target-ssh.getstate-1.json | 2 +- ...atrix-components.new-workspace-repositories-repo.list-1.json | 2 +- .../goldens/matrix-components.setup-script-repo.hooks-1.json | 2 +- .../goldens/matrix-files.explorer-screen-files.list-1.json | 2 +- .../goldens/matrix-files.explorer-screen-files.readdir-1.json | 2 +- .../goldens/matrix-files.mutation-ownership-ssh.getstate-1.json | 2 +- .../goldens/matrix-files.mutation-ownership-status.get-1.json | 2 +- .../matrix-files.mutation-ownership-worktree.show-1.json | 2 +- ...view-artifact-image-files.readterminalartifactpreview-1.json | 2 +- .../matrix-files.preview-load-files.readterminalartifact-1.json | 2 +- .../matrix-files.preview-load-files.readterminalartifact-2.json | 2 +- .../matrix-files.preview-load-files.resolveterminalpath-1.json | 2 +- .../matrix-files.preview-save-files.readterminalartifact-1.json | 2 +- ...matrix-files.preview-save-files.writeterminalartifact-1.json | 2 +- ...matrix-files.preview-worktree-image-files.readpreview-1.json | 2 +- .../matrix-files.preview-worktree-text-files.read-1.json | 2 +- .../goldens/matrix-files.tab-doc-files.read-1.json | 2 +- .../goldens/matrix-files.tab-doc-files.readpreview-1.json | 2 +- .../rpc-foundation/goldens/matrix-files.tab-doc-git.diff-1.json | 2 +- .../goldens/matrix-files.terminal-path-tap-files.open-1.json | 2 +- ...rix-files.terminal-path-tap-files.resolveterminalpath-1.json | 2 +- .../matrix-git.base-ref-chain-repo.baserefdefault-1.json | 2 +- .../goldens/matrix-git.base-ref-chain-repo.list-1.json | 2 +- .../goldens/matrix-git.base-ref-chain-worktree.show-1.json | 2 +- .../matrix-git.branch-diff-preview-git.branchdiff-1.json | 2 +- .../goldens/matrix-git.changes-load-git.branchcompare-1.json | 2 +- .../goldens/matrix-git.changes-load-git.status-1.json | 2 +- .../goldens/matrix-git.changes-load-repo.list-1.json | 2 +- .../goldens/matrix-git.changes-load-worktree.show-1.json | 2 +- ...atrix-git.commit-message-ai-git.generatecommitmessage-1.json | 2 +- .../matrix-git.history-commit-files-git.commitcompare-1.json | 2 +- .../goldens/matrix-git.history-commit-files-git.history-1.json | 2 +- .../goldens/matrix-git.history-read-git.history-1.json | 2 +- .../goldens/matrix-git.remote-prerequisite-git.push-1.json | 2 +- .../goldens/matrix-git.review-preparation-git.status-1.json | 2 +- ...rix-github.pr-comment-mutation-github.addissuecomment-1.json | 2 +- ...ub.pr-comment-mutation-github.addprreviewcommentreply-1.json | 2 +- ...ment-mutation-github.project.deleteissuecommentbyslug-1.json | 2 +- ...ment-mutation-github.project.updateissuecommentbyslug-1.json | 2 +- ...github.pr-comment-mutation-github.resolvereviewthread-1.json | 2 +- .../goldens/matrix-github.pr-mutation-github.mergepr-1.json | 2 +- .../matrix-github.pr-mutation-github.removeprreviewers-1.json | 2 +- .../matrix-github.pr-mutation-github.requestprreviewers-1.json | 2 +- .../matrix-github.pr-mutation-github.rerunprchecks-1.json | 2 +- .../matrix-github.pr-mutation-github.setprautomerge-1.json | 2 +- .../matrix-github.pr-mutation-github.updateprstate-1.json | 2 +- .../matrix-github.pr-read-github.listassignableusers-1.json | 2 +- .../goldens/matrix-github.pr-read-github.prcheckdetails-1.json | 2 +- .../goldens/matrix-github.pr-read-github.prchecks-1.json | 2 +- .../goldens/matrix-github.pr-read-github.prforbranch-1.json | 2 +- .../goldens/matrix-github.pr-read-github.reposlug-1.json | 2 +- .../goldens/matrix-github.pr-read-github.workitemdetails-1.json | 2 +- .../goldens/matrix-github.pr-read-hostedreview.forbranch-1.json | 2 +- .../matrix-github.pr-title-mutation-github.updateprtitle-1.json | 2 +- .../goldens/matrix-home.host-accounts-accounts.list-1.json | 2 +- .../goldens/matrix-home.host-stats-stats.summary-1.json | 2 +- ...ost-worktree-refresh-runtime.clientevents.subscribe-1-1.json | 2 +- ...ost-worktree-refresh-runtime.clientevents.subscribe-1-2.json | 2 +- ...ost-worktree-refresh-runtime.clientevents.subscribe-1-3.json | 2 +- ...ost-worktree-refresh-runtime.clientevents.subscribe-2-1.json | 2 +- .../goldens/matrix-host.view-settings-ui.get-1.json | 2 +- .../goldens/matrix-host.view-settings-ui.set-1.json | 2 +- .../matrix-host.worktree-actions-worktree.activate-1.json | 2 +- .../goldens/matrix-host.worktree-actions-worktree.rm-1.json | 2 +- .../goldens/matrix-host.worktree-actions-worktree.set-1.json | 2 +- .../goldens/matrix-hostedreview.create-chain-git.push-1.json | 2 +- .../matrix-hostedreview.create-chain-hostedreview.create-1.json | 2 +- .../matrix-hostedreview.create-chain-worktree.set-1.json | 2 +- .../matrix-hostedreview.create-intent-git.bulkstage-1.json | 2 +- .../goldens/matrix-hostedreview.create-intent-git.commit-1.json | 2 +- ...-hostedreview.create-intent-git.generatecommitmessage-1.json | 2 +- .../goldens/matrix-hostedreview.create-intent-git.push-1.json | 2 +- .../goldens/matrix-hostedreview.create-intent-git.status-1.json | 2 +- .../goldens/matrix-hostedreview.create-intent-git.status-2.json | 2 +- .../goldens/matrix-hostedreview.create-intent-git.status-3.json | 2 +- .../goldens/matrix-hostedreview.create-intent-git.status-4.json | 2 +- ...matrix-hostedreview.create-intent-hostedreview.create-1.json | 2 +- ...iew.create-intent-hostedreview.getcreationeligibility-1.json | 2 +- ...iew.create-intent-hostedreview.getcreationeligibility-2.json | 2 +- .../matrix-hostedreview.create-intent-worktree.set-1.json | 2 +- ...eview.eligibility-hostedreview.getcreationeligibility-1.json | 2 +- .../goldens/matrix-legacy-inventory-files.searchpaths-1.json | 2 +- .../goldens/matrix-legacy-inventory-files.searchpaths-2.json | 2 +- .../goldens/matrix-legacy-inventory-fresh-inventory.json | 2 +- .../goldens/matrix-legacy-inventory-old-inventory.json | 2 +- .../goldens/matrix-linear-detail-barrier-linear.getissue-1.json | 2 +- .../matrix-linear-detail-barrier-linear.issuecomments-1.json | 2 +- ...linear.select-workspace-picker-linear.selectworkspace-1.json | 2 +- ...x-live-worktree-name-runtime.clientevents.subscribe-1-1.json | 2 +- ...x-live-worktree-name-runtime.clientevents.subscribe-1-2.json | 2 +- ...x-live-worktree-name-runtime.clientevents.subscribe-2-1.json | 2 +- .../goldens/matrix-live-worktree-name-worktree.show-1.json | 2 +- .../goldens/matrix-live-worktree-name-worktree.show-2.json | 2 +- .../goldens/matrix-live-worktree-name-worktree.show-3.json | 2 +- .../goldens/matrix-nativechat.image-paste-terminal.send-1.json | 2 +- .../goldens/matrix-nativechat.image-paste-terminal.send-2.json | 2 +- ...ix-nativechat.image-upload-clipboard.startimageupload-1.json | 2 +- ...n-option-pick-settings.mutatenativechatsessionoptions-1.json | 2 +- ....terminal-write-orchestration.workerterminaluserinput-1.json | 2 +- .../matrix-nativechat.terminal-write-terminal.send-1.json | 2 +- ...fications.desktop-stream-notifications.getmissedsince-1.json | 2 +- ...otifications.desktop-stream-notifications.subscribe-1-1.json | 2 +- ...otifications.desktop-stream-notifications.subscribe-1-2.json | 2 +- ...otifications.desktop-stream-notifications.unsubscribe-1.json | 2 +- ...ifications.display-test-screen-notifications.testpush-1.json | 2 +- ...fications.push-dismissal-notifications.getmissedsince-1.json | 2 +- ...ications.push-registration-notifications.registerpush-1.json | 2 +- ...ations.push-registration-notifications.unregisterpush-1.json | 2 +- .../goldens/matrix-pairing.pre-profile-direct-status.json | 2 +- .../matrix-pairing.pre-profile-pairing.getendpoints-1.json | 2 +- .../matrix-pairing.pre-profile-pairing.provisionrelay-1.json | 2 +- .../goldens/matrix-pairing.pre-profile-relay-status.json | 2 +- ...oject-explicit-false-github.project.updateissuebyslug-1.json | 2 +- ...matrix-relay.credential-rotation-pairing.getendpoints-1.json | 2 +- ...matrix-relay.credential-rotation-pairing.getendpoints-2.json | 2 +- ...trix-relay.credential-rotation-pairing.provisionrelay-1.json | 2 +- .../matrix-relay.direct-upgrade-pairing.getendpoints-1.json | 2 +- .../matrix-relay.direct-upgrade-pairing.getendpoints-2.json | 2 +- .../matrix-relay.direct-upgrade-pairing.provisionrelay-1.json | 2 +- .../matrix-relay.pairing-recovery-pairing.getendpoints-1.json | 2 +- .../matrix-session.browser-tab-create-browser.tabcreate-1.json | 2 +- .../matrix-session.content-create-files.createfile-1.json | 2 +- .../goldens/matrix-session.content-create-files.open-1.json | 2 +- .../goldens/matrix-session.content-create-status.get-1.json | 2 +- .../goldens/matrix-session.content-create-worktree.show-1.json | 2 +- ...x-session.create-terminal-session.tabs.createterminal-1.json | 2 +- .../goldens/matrix-session.create-terminal-terminal.send-1.json | 2 +- .../goldens/matrix-session.diff-notes-worktree.show-1.json | 2 +- .../matrix-session.diff-review-actions-worktree.set-1.json | 2 +- .../goldens/matrix-session.diff-review-base-ref-show.json | 2 +- .../goldens/matrix-session.diff-review-git.branchcompare-1.json | 2 +- .../goldens/matrix-session.diff-review-git.status-1.json | 2 +- .../goldens/matrix-session.diff-review-repo.list-1.json | 2 +- .../goldens/matrix-session.diff-review-review-show.json | 2 +- .../matrix-session.markdown-disk-fallback-files.read-1.json | 2 +- ...atrix-session.markdown-disk-fallback-markdown.readtab-1.json | 2 +- .../matrix-session.markdown-save-markdown.savetab-1.json | 2 +- ...atrix-session.native-chat-page-nativechat.readsession-1.json | 2 +- ...atrix-session.native-chat-page-nativechat.subscribe-1-1.json | 2 +- ...atrix-session.native-chat-page-nativechat.subscribe-2-1.json | 2 +- .../matrix-session.native-chat-readability-repo.list-1.json | 2 +- ...ative-chat-stop-orchestration.workerterminaluserinput-1.json | 2 +- .../matrix-session.native-chat-stop-terminal.send-1.json | 2 +- .../matrix-session.native-chat-stop-terminal.send-2.json | 2 +- .../matrix-session.pr-branch-context-git.branchcompare-1.json | 2 +- .../goldens/matrix-session.pr-branch-context-git.status-1.json | 2 +- .../goldens/matrix-session.pr-branch-context-repo.list-1.json | 2 +- .../matrix-session.pr-branch-context-worktree.show-1.json | 2 +- .../goldens/matrix-session.pr-sidebar-github.prchecks-1.json | 2 +- .../goldens/matrix-session.pr-sidebar-github.prforbranch-1.json | 2 +- .../matrix-session.pr-sidebar-hostedreview.forbranch-1.json | 2 +- .../goldens/matrix-session.pr-sidebar-worktree.show-1.json | 2 +- .../matrix-session.pr-triage-session.tabs.createterminal-1.json | 2 +- .../goldens/matrix-session.pr-triage-terminal.send-1.json | 2 +- .../matrix-session.review-branch-diff-git.branchdiff-1.json | 2 +- .../goldens/matrix-session.review-file-diff-git.diff-1.json | 2 +- .../goldens/matrix-session.review-file-diff-git.diff-2.json | 2 +- .../goldens/matrix-session.review-file-diff-git.diff-3.json | 2 +- .../matrix-session.review-git-mutations-git.discard-1.json | 2 +- .../matrix-session.review-git-mutations-git.stage-1.json | 2 +- .../matrix-session.review-git-mutations-git.stage-2.json | 2 +- .../matrix-session.review-send-sheet-session.tabs.list-1.json | 2 +- .../goldens/matrix-session.startup-worktree.activate-1.json | 2 +- .../goldens/matrix-session.startup-worktree.activate-2.json | 2 +- .../matrix-session.tab-activation-session.tabs.activate-1.json | 2 +- .../goldens/matrix-session.tab-activation-terminal.focus-1.json | 2 +- .../matrix-session.tab-close-session-session.tabs.close-1.json | 2 +- .../goldens/matrix-session.tab-close-terminal.close-1.json | 2 +- .../matrix-session.tab-documents-markdown.readtab-1.json | 2 +- .../goldens/matrix-session.tab-rename-terminal.rename-1.json | 2 +- .../matrix-session.tab-reveal-session.tabs.activate-1.json | 2 +- .../goldens/matrix-session.tab-reveal-session.tabs.list-1.json | 2 +- .../matrix-session.tabs-stream-health-session.tabs.list-1.json | 2 +- ...session.terminal-display-mode-terminal.setdisplaymode-1.json | 2 +- ...l-gesture-input-orchestration.workerterminaluserinput-1.json | 2 +- ...x-session.terminal-gesture-input-terminal.clearbuffer-1.json | 2 +- .../matrix-session.terminal-gesture-input-terminal.send-1.json | 2 +- ...inal-input-send-orchestration.workerterminaluserinput-1.json | 2 +- .../matrix-session.terminal-input-send-terminal.send-1.json | 2 +- .../matrix-session.terminal-inventory-terminal.list-1.json | 2 +- ....terminal-paste-orchestration.workerterminaluserinput-1.json | 2 +- .../goldens/matrix-session.terminal-paste-settings.get-1.json | 2 +- .../goldens/matrix-session.terminal-paste-terminal.send-1.json | 2 +- .../goldens/matrix-session.worktree-connection-repo.list-1.json | 2 +- .../matrix-session.worktree-connection-settings.get-1.json | 2 +- ...trix-settings-agent-read-preflight.detectremoteagents-1.json | 2 +- .../goldens/matrix-settings-agent-read-repo.list-1.json | 2 +- .../goldens/matrix-settings-agent-read-settings.get-1.json | 2 +- .../goldens/matrix-settings-best-effort-settings.update-1.json | 2 +- .../goldens/matrix-settings.bot-overrides-settings.get-1.json | 2 +- .../goldens/matrix-settings.home-providers-linear.status-1.json | 2 +- .../matrix-settings.home-providers-preflight.check-1.json | 2 +- .../goldens/matrix-settings.home-providers-settings.get-1.json | 2 +- ...-settings.new-tab-local-agents-preflight.detectagents-1.json | 2 +- .../matrix-settings.new-tab-local-agents-repo.list-1.json | 2 +- .../matrix-settings.new-tab-local-agents-settings.get-1.json | 2 +- ...ings.quick-commands-settings.getterminalquickcommands-1.json | 2 +- ...s.quick-commands-settings.updateterminalquickcommands-1.json | 2 +- .../goldens/matrix-settings.repo-metadata-host.platform-1.json | 2 +- .../goldens/matrix-settings.repo-metadata-repo.list-1.json | 2 +- .../goldens/matrix-settings.repo-metadata-settings.get-1.json | 2 +- ...matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json | 2 +- .../matrix-settings.resume-metadata-folderworkspace.list-1.json | 2 +- .../matrix-settings.resume-metadata-projectgroup.list-1.json | 2 +- .../goldens/matrix-settings.resume-metadata-repo.list-1.json | 2 +- .../goldens/matrix-settings.resume-metadata-settings.get-1.json | 2 +- .../goldens/matrix-settings.resume-metadata-worktree.ps-1.json | 2 +- .../goldens/matrix-settings.task-hydration-linear.status-1.json | 2 +- .../matrix-settings.task-hydration-preflight.check-1.json | 2 +- .../goldens/matrix-settings.task-hydration-settings.get-1.json | 2 +- .../goldens/matrix-settings.task-hydration-status.get-1.json | 2 +- .../goldens/matrix-settings.task-hydration-ui.get-1.json | 2 +- .../matrix-settings.task-workspace-create-settings.get-1.json | 2 +- ...matrix-settings.task-workspace-create-worktree.create-1.json | 2 +- .../goldens/matrix-settings.task-workspace-settings.get-1.json | 2 +- .../matrix-settings.workspace-context-linear.status-1.json | 2 +- .../matrix-settings.workspace-context-preflight.check-1.json | 2 +- .../matrix-settings.workspace-context-settings.get-1.json | 2 +- .../goldens/matrix-settings.workspace-context-ui.get-1.json | 2 +- .../matrix-settings.workspace-submit-settings.get-1.json | 2 +- .../matrix-speech.dictation-chunk-speech.dictation.chunk-1.json | 2 +- ...trix-speech.dictation-session-speech.dictation.finish-1.json | 2 +- ...atrix-speech.dictation-session-speech.dictation.start-1.json | 2 +- ...matrix-speech.dictation-start-speech.dictation.cancel-1.json | 2 +- .../matrix-speech.dictation-start-speech.dictation.start-1.json | 2 +- .../matrix-speech.setup-sheet-speech.dictation.setup-1.json | 2 +- .../matrix-speech.setup-sheet-speech.models.delete-1.json | 2 +- .../matrix-speech.setup-sheet-speech.models.download-1.json | 2 +- .../goldens/matrix-speech.setup-sheet-speech.models.list-1.json | 2 +- ...rix-tasks.item-checks-files-github.addprreviewcomment-1.json | 2 +- .../matrix-tasks.item-checks-files-github.prfilecontents-1.json | 2 +- .../matrix-tasks.item-checks-files-github.rerunprchecks-1.json | 2 +- ...ix-tasks.item-checks-files-github.resolvereviewthread-1.json | 2 +- ...matrix-tasks.item-checks-files-github.setprfileviewed-1.json | 2 +- ...trix-tasks.item-comment-github-github.addissuecomment-1.json | 2 +- ...trix-tasks.item-comment-gitlab-gitlab.addissuecomment-1.json | 2 +- ...trix-tasks.item-comment-gitlab-mr-gitlab.addmrcomment-1.json | 2 +- ...atrix-tasks.item-detail-github-github.workitemdetails-1.json | 2 +- ...atrix-tasks.item-detail-gitlab-gitlab.workitemdetails-1.json | 2 +- .../matrix-tasks.item-detail-linear-linear.getissue-1.json | 2 +- .../matrix-tasks.item-detail-linear-linear.issuecomments-1.json | 2 +- ...tasks.item-detail-metadata-github.listassignableusers-1.json | 2 +- .../matrix-tasks.item-detail-metadata-github.listlabels-1.json | 2 +- .../matrix-tasks.item-merge-gitlab-gitlab.mergemr-1.json | 2 +- .../matrix-tasks.item-metadata-github-github.updatepr-1.json | 2 +- .../matrix-tasks.item-metadata-gitlab-gitlab.updateissue-1.json | 2 +- .../matrix-tasks.item-metadata-gitlab-mr-gitlab.updatemr-1.json | 2 +- .../matrix-tasks.item-reply-merge-github.addissuecomment-1.json | 2 +- ...tasks.item-reply-merge-github.addprreviewcommentreply-1.json | 2 +- .../goldens/matrix-tasks.item-reply-merge-github.mergepr-1.json | 2 +- .../matrix-tasks.item-reply-merge-linear.updateissue-1.json | 2 +- .../matrix-tasks.item-review-github-github.prchecks-1.json | 2 +- ...ix-tasks.item-review-github-github.requestprreviewers-1.json | 2 +- .../matrix-tasks.item-status-gitlab-github.updateissue-1.json | 2 +- .../matrix-tasks.item-status-gitlab-gitlab.updateissue-1.json | 2 +- ...trix-tasks.item-status-gitlab-mr-gitlab.updatemrstate-1.json | 2 +- .../goldens/matrix-tasks.linear-connect-linear.connect-1.json | 2 +- .../matrix-tasks.linear-item-linear.addissuecomment-1.json | 2 +- .../goldens/matrix-tasks.linear-item-linear.createissue-1.json | 2 +- .../goldens/matrix-tasks.linear-item-linear.getissue-1.json | 2 +- .../matrix-tasks.linear-team-context-linear.listteams-1.json | 2 +- .../matrix-tasks.linear-team-context-linear.teamstates-1.json | 2 +- .../goldens/matrix-tasks.paste-lookup-github.reposlug-1.json | 2 +- .../goldens/matrix-tasks.paste-lookup-github.workitem-1.json | 2 +- .../matrix-tasks.paste-lookup-github.workitembyownerrepo-1.json | 2 +- .../matrix-tasks.paste-lookup-gitlab.workitembypath-1.json | 2 +- ...asks.project-board-load-github.project.listaccessible-1.json | 2 +- ...rix-tasks.project-board-load-github.project.listviews-1.json | 2 +- ...rix-tasks.project-board-load-github.project.listviews-2.json | 2 +- ...ix-tasks.project-board-load-github.project.resolveref-1.json | 2 +- ...rix-tasks.project-board-load-github.project.viewtable-1.json | 2 +- .../matrix-tasks.project-repo-slugs-github.reposlug-1.json | 2 +- ...w-comments-issue-github.project.addissuecommentbyslug-1.json | 2 +- ...t-row-comments-issue-github.project.updateissuebyslug-1.json | 2 +- ...omments-issue-github.project.updateissuecommentbyslug-1.json | 2 +- ...ow-comments-pr-github.project.updatepullrequestbyslug-1.json | 2 +- ...oject-row-detail-github.project.workitemdetailsbyslug-1.json | 2 +- ...asks.project-row-fields-github.project.clearitemfield-1.json | 2 +- ...oject-row-fields-github.project.updateissuetypebyslug-1.json | 2 +- ...sks.project-row-fields-github.project.updateitemfield-1.json | 2 +- ...sks.project-row-files-merge-github.addprreviewcomment-1.json | 2 +- .../matrix-tasks.project-row-files-merge-github.mergepr-1.json | 2 +- ...x-tasks.project-row-files-merge-github.prfilecontents-1.json | 2 +- ...trix-tasks.project-row-files-merge-github.updateissue-1.json | 2 +- ...ix-tasks.project-row-files-merge-github.updateprstate-1.json | 2 +- ...etadata-load-github.project.listassignableusersbyslug-1.json | 2 +- ...row-metadata-load-github.project.listissuetypesbyslug-1.json | 2 +- ...ect-row-metadata-load-github.project.listlabelsbyslug-1.json | 2 +- ...atrix-tasks.project-row-review-checks-github.prchecks-1.json | 2 +- ...s.project-row-review-checks-github.requestprreviewers-1.json | 2 +- ...-tasks.project-row-review-checks-github.rerunprchecks-1.json | 2 +- ...asks.project-row-review-checks-github.setprfileviewed-1.json | 2 +- ...trix-tasks.project-row-threads-github.addissuecomment-1.json | 2 +- ...ks.project-row-threads-github.addprreviewcommentreply-1.json | 2 +- ...t-row-threads-github.project.deleteissuecommentbyslug-1.json | 2 +- ...-tasks.project-row-threads-github.resolvereviewthread-1.json | 2 +- .../matrix-tasks.provider-load-github.countworkitems-1.json | 2 +- .../matrix-tasks.provider-load-github.listworkitems-1.json | 2 +- .../goldens/matrix-tasks.provider-load-linear.listteams-1.json | 2 +- .../goldens/matrix-tasks.provider-load-linear.status-1.json | 2 +- .../goldens/matrix-tasks.provider-load-settings.update-1.json | 2 +- .../goldens/matrix-tasks.route-repo-list-repo.list-1.json | 2 +- ...matrix-tasks.smart-source-search-github.listworkitems-1.json | 2 +- ...matrix-tasks.smart-source-search-gitlab.listworkitems-1.json | 2 +- .../matrix-tasks.smart-source-search-linear.listissues-1.json | 2 +- .../matrix-tasks.smart-source-search-linear.searchissues-1.json | 2 +- .../matrix-tasks.smart-source-search-repo.searchrefs-1.json | 2 +- .../matrix-tasks.task-create-github-github.createissue-1.json | 2 +- .../goldens/matrix-tasks.task-create-github-repo.update-1.json | 2 +- .../matrix-tasks.task-create-gitlab-gitlab.createissue-1.json | 2 +- .../matrix-tasks.task-create-linear-linear.createissue-1.json | 2 +- ...rix-tasks.task-list-gitlab-items-gitlab.listworkitems-1.json | 2 +- .../matrix-tasks.task-list-gitlab-todos-gitlab.todos-1.json | 2 +- .../matrix-tasks.task-list-linear-linear.listissues-1.json | 2 +- .../matrix-tasks.task-list-linear-linear.searchissues-1.json | 2 +- .../matrix-tasks.workspace-source-repo.searchrefs-1.json | 2 +- .../matrix-tasks.workspace-source-repo.sparsepresets-1.json | 2 +- .../matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json | 2 +- .../goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json | 2 +- ...trix-tasks.workspace-ssh-local-preflight.detectagents-1.json | 2 +- ...trix-tasks.workspace-ssh-preflight.detectremoteagents-1.json | 2 +- .../goldens/matrix-tasks.workspace-ssh-repo.hooks-1.json | 2 +- .../goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json | 2 +- .../goldens/matrix-terminal.query-reply-terminal.send-1.json | 2 +- ...minal.raw-input-orchestration.workerterminaluserinput-1.json | 2 +- .../goldens/matrix-terminal.raw-input-terminal.send-1.json | 2 +- ...takeover-report-orchestration.workerterminaluserinput-1.json | 2 +- ...takeover-report-orchestration.workerterminaluserinput-2.json | 2 +- ...atrix-terminal.viewport-refit-terminal.updateviewport-1.json | 2 +- .../goldens/matrix-transport.capability-probe-status.get-1.json | 2 +- .../matrix-transport.host-status-gates-status.get-1.json | 2 +- .../goldens/matrix-transport.pairing-race-direct-status.json | 2 +- .../goldens/matrix-transport.pairing-race-relay-status.json | 2 +- .../matrix-worktree.agent-launch-create-agent.launch-1.json | 2 +- .../goldens/matrix-worktree.catalog-snapshot-worktree.ps-1.json | 2 +- .../goldens/matrix-worktree.create-retry-worktree.create-1.json | 2 +- .../goldens/matrix-worktree.home-catalog-worktree.ps-1.json | 2 +- .../matrix-worktree.hosted-base-worktree.resolvemrbase-1.json | 2 +- .../matrix-worktree.hosted-base-worktree.resolveprbase-1.json | 2 +- ...trix-worktree.retired-names-worktree.listretirednames-1.json | 2 +- .../goldens/matrix-worktree.review-link-worktree.set-1.json | 2 +- .../matrix-worktree.runtime-capabilities-status.get-1.json | 2 +- .../goldens/matrix-worktree.setup-hook-trust-ui.set-1.json | 2 +- .../rpc-foundation/goldens/native-chat-image-paste-single.json | 2 +- .../goldens/native-chat-image-paste-stops-on-rejection.json | 2 +- .../goldens/native-chat-image-paste-trailing-image.json | 2 +- .../goldens/native-chat-image-paste-two-images.json | 2 +- .../goldens/native-chat-image-upload-cancelled.json | 2 +- .../goldens/native-chat-image-upload-second-fails.json | 2 +- .../rpc-foundation/goldens/native-chat-image-upload-single.json | 2 +- .../goldens/native-chat-image-upload-start-refused.json | 2 +- mobile/rpc-foundation/goldens/native-chat-image-upload-two.json | 2 +- mobile/rpc-foundation/goldens/native-chat-page-earlier.json | 2 +- .../goldens/native-chat-readability-local-repo.json | 2 +- .../rpc-foundation/goldens/native-chat-readability-refused.json | 2 +- .../goldens/native-chat-readability-remote-repo.json | 2 +- .../goldens/native-chat-session-option-pick-empty.json | 2 +- .../goldens/native-chat-session-option-pick-refused.json | 2 +- .../goldens/native-chat-session-option-pick-written.json | 2 +- mobile/rpc-foundation/goldens/native-chat-stop-accepted.json | 2 +- .../rpc-foundation/goldens/native-chat-stop-both-rejected.json | 2 +- .../goldens/native-chat-stop-delivery-unknown.json | 2 +- mobile/rpc-foundation/goldens/native-chat-write-accepted.json | 2 +- mobile/rpc-foundation/goldens/native-chat-write-clear-line.json | 2 +- .../goldens/native-chat-write-delivery-unknown.json | 2 +- mobile/rpc-foundation/goldens/native-chat-write-rejected.json | 2 +- .../rpc-foundation/goldens/native-chat-write-typed-command.json | 2 +- mobile/rpc-foundation/goldens/new-tab-local-agents.json | 2 +- .../goldens/new-workspace-repositories-fulfilled.json | 2 +- .../goldens/notifications-desktop-stream-closed.json | 2 +- .../goldens/notifications-desktop-stream-replayed.json | 2 +- mobile/rpc-foundation/goldens/notifications-desktop-stream.json | 2 +- .../goldens/notifications-display-test-accepted.json | 2 +- .../goldens/notifications-display-test-not-registered.json | 2 +- .../goldens/notifications-display-test-rate-limited.json | 2 +- .../goldens/notifications-display-test-unknown-reason.json | 2 +- .../goldens/notifications-push-gateway-rejected.json | 2 +- .../rpc-foundation/goldens/notifications-push-registered.json | 2 +- .../goldens/pairing-pre-profile-direct-wins-and-provisions.json | 2 +- ...ing-pre-profile-provision-unsupported-saves-direct-host.json | 2 +- .../rpc-foundation/goldens/pairing-pre-profile-times-out.json | 2 +- mobile/rpc-foundation/goldens/pr-branch-identity.json | 2 +- mobile/rpc-foundation/goldens/pr-branch-repo-context.json | 2 +- mobile/rpc-foundation/goldens/pr-comment-mutation.json | 2 +- .../rpc-foundation/goldens/pr-comment-resolve-unconfirmed.json | 2 +- mobile/rpc-foundation/goldens/pr-mutation-in-band-failure.json | 2 +- mobile/rpc-foundation/goldens/pr-mutation-status.json | 2 +- mobile/rpc-foundation/goldens/pr-read-fork-routing.json | 2 +- mobile/rpc-foundation/goldens/pr-read-surface.json | 2 +- mobile/rpc-foundation/goldens/pr-read-upstream-error.json | 2 +- mobile/rpc-foundation/goldens/pr-sidebar-checks-refused.json | 2 +- mobile/rpc-foundation/goldens/pr-sidebar-load.json | 2 +- mobile/rpc-foundation/goldens/pr-title-mutation.json | 2 +- mobile/rpc-foundation/goldens/pr-title-unconfirmed.json | 2 +- mobile/rpc-foundation/goldens/pr-triage-invalid-terminal.json | 2 +- mobile/rpc-foundation/goldens/pr-triage-launch.json | 2 +- mobile/rpc-foundation/goldens/pr-triage-send-locked.json | 2 +- mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json | 2 +- .../goldens/probe-new-tab-null-sibling-refused.json | 2 +- .../goldens/probe-new-tab-refused-sibling-rejects.json | 2 +- .../goldens/probe-new-tab-rejects-sibling-refused.json | 2 +- .../rpc-foundation/goldens/push-dismissal-tray-reconciled.json | 2 +- mobile/rpc-foundation/goldens/quick-commands-load-refused.json | 2 +- .../rpc-foundation/goldens/quick-commands-loaded-and-saved.json | 2 +- .../goldens/quick-commands-save-refused-rolls-back.json | 2 +- mobile/rpc-foundation/goldens/relay-direct-upgrade-commits.json | 2 +- .../goldens/relay-direct-upgrade-unsupported-host-declines.json | 2 +- .../goldens/relay-pairing-recovery-invite-authorizes.json | 2 +- .../goldens/relay-pairing-recovery-resume-committed.json | 2 +- .../goldens/relay-rotation-installs-and-commits.json | 2 +- .../goldens/relay-rotation-resumes-committed-pending.json | 2 +- mobile/rpc-foundation/goldens/review-branch-diff-shapes.json | 2 +- .../rpc-foundation/goldens/review-create-terminal-refused.json | 2 +- mobile/rpc-foundation/goldens/review-file-diff-shapes.json | 2 +- mobile/rpc-foundation/goldens/review-git-mutations-run.json | 2 +- .../rpc-foundation/goldens/review-mark-reviewed-persists.json | 2 +- .../rpc-foundation/goldens/review-mark-reviewed-rolls-back.json | 2 +- mobile/rpc-foundation/goldens/review-open-in-session.json | 2 +- .../goldens/review-send-notes-heals-stale-input.json | 2 +- .../goldens/review-send-sheet-lists-terminals.json | 2 +- mobile/rpc-foundation/goldens/review-stage-file.json | 2 +- mobile/rpc-foundation/goldens/review-stage-refused.json | 2 +- mobile/rpc-foundation/goldens/sc-base-ref-default.json | 2 +- mobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.json | 2 +- mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json | 2 +- mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json | 2 +- mobile/rpc-foundation/goldens/sc-branch-diff-previewed.json | 2 +- mobile/rpc-foundation/goldens/sc-changes-loaded.json | 2 +- .../goldens/sc-commit-message-cancel-rejected.json | 2 +- mobile/rpc-foundation/goldens/sc-commit-message-canceled.json | 2 +- mobile/rpc-foundation/goldens/sc-commit-message-generated.json | 2 +- mobile/rpc-foundation/goldens/sc-create-existing-review.json | 2 +- .../goldens/sc-create-intent-stage-commit-push-create.json | 2 +- .../goldens/sc-create-intent-unlisted-provider.json | 2 +- .../goldens/sc-create-link-failure-is-non-fatal.json | 2 +- .../rpc-foundation/goldens/sc-create-pushes-then-creates.json | 2 +- .../rpc-foundation/goldens/sc-create-refused-empty-message.json | 2 +- .../goldens/sc-create-rejected-empty-message.json | 2 +- mobile/rpc-foundation/goldens/sc-eligibility-fetched.json | 2 +- mobile/rpc-foundation/goldens/sc-history-commit-files.json | 2 +- mobile/rpc-foundation/goldens/sc-history-loaded.json | 2 +- mobile/rpc-foundation/goldens/sc-pr-link-hosted-review.json | 2 +- mobile/rpc-foundation/goldens/sc-pr-link-read.json | 2 +- mobile/rpc-foundation/goldens/sc-pr-link-set.json | 2 +- .../goldens/sc-prefill-unavailable-on-refusal.json | 2 +- .../goldens/sc-prefill-unavailable-on-rejection.json | 2 +- .../goldens/sc-prerequisite-force-with-lease.json | 2 +- mobile/rpc-foundation/goldens/sc-prerequisite-publish.json | 2 +- mobile/rpc-foundation/goldens/sc-prerequisite-push.json | 2 +- mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json | 2 +- mobile/rpc-foundation/goldens/sc-reveal-first-poll.json | 2 +- mobile/rpc-foundation/goldens/sc-reveal-timeout.json | 2 +- .../rpc-foundation/goldens/sc-review-commit-inner-failure.json | 2 +- .../goldens/sc-review-commit-refused-empty-message.json | 2 +- mobile/rpc-foundation/goldens/sc-review-commit-rejected.json | 2 +- mobile/rpc-foundation/goldens/sc-review-commit.json | 2 +- .../goldens/sc-review-status-entries-not-array.json | 2 +- mobile/rpc-foundation/goldens/sc-review-status-normalized.json | 2 +- mobile/rpc-foundation/goldens/schedules-b3.json | 2 +- .../goldens/schedules-settings-home-providers-fulfilled.json | 2 +- .../rpc-foundation/goldens/schedules-settings-new-tab-ssh.json | 2 +- .../goldens/schedules-settings-repo-metadata-fulfilled.json | 2 +- .../goldens/schedules-settings-resume-metadata-fulfilled.json | 2 +- .../goldens/schedules-settings-task-hydration-fulfilled.json | 2 +- .../goldens/schedules-settings-workspace-context-fulfilled.json | 2 +- mobile/rpc-foundation/goldens/session-browser-tab-created.json | 2 +- .../rpc-foundation/goldens/session-create-browser-refused.json | 2 +- mobile/rpc-foundation/goldens/session-create-browser-tab.json | 2 +- .../goldens/session-create-markdown-name-collision.json | 2 +- mobile/rpc-foundation/goldens/session-create-markdown-note.json | 2 +- ...ssion-create-terminal-ignores-a-second-create-in-flight.json | 2 +- ...session-create-terminal-launches-an-agent-quick-command.json | 2 +- .../rpc-foundation/goldens/session-create-terminal-refused.json | 2 +- .../goldens/session-create-terminal-replaces-active.json | 2 +- .../goldens/session-create-terminal-runs-a-quick-command.json | 2 +- .../goldens/session-create-terminal-with-prompt.json | 2 +- .../goldens/session-create-terminal-without-active-tab.json | 2 +- .../goldens/session-create-terminal-without-handle.json | 2 +- .../rpc-foundation/goldens/session-diff-notes-load-refused.json | 2 +- mobile/rpc-foundation/goldens/session-diff-notes-loaded.json | 2 +- mobile/rpc-foundation/goldens/session-file-tab-read.json | 2 +- mobile/rpc-foundation/goldens/session-markdown-disk-read.json | 2 +- mobile/rpc-foundation/goldens/session-markdown-disk-served.json | 2 +- .../rpc-foundation/goldens/session-markdown-save-conflict.json | 2 +- mobile/rpc-foundation/goldens/session-markdown-saved.json | 2 +- .../goldens/session-markdown-tab-disk-fallback.json | 2 +- mobile/rpc-foundation/goldens/session-markdown-tab-read.json | 2 +- mobile/rpc-foundation/goldens/session-markdown-tab-refused.json | 2 +- .../goldens/session-startup-both-activation-sites.json | 2 +- .../session-startup-floating-route-skips-activation.json | 2 +- .../session-startup-keeps-terminals-visible-on-reconnect.json | 2 +- .../session-startup-refused-tab-load-still-loads-terminals.json | 2 +- .../goldens/session-tab-activation-focus-and-activate.json | 2 +- .../rpc-foundation/goldens/session-tab-activation-refused.json | 2 +- .../goldens/session-tab-activation-transport-error.json | 2 +- .../goldens/session-tab-close-refused-keeps-tab.json | 2 +- .../rpc-foundation/goldens/session-tab-close-session-tab.json | 2 +- mobile/rpc-foundation/goldens/session-tab-close-terminal.json | 2 +- mobile/rpc-foundation/goldens/session-tab-closed.json | 2 +- mobile/rpc-foundation/goldens/session-tab-rename.json | 2 +- mobile/rpc-foundation/goldens/session-tab-renamed.json | 2 +- mobile/rpc-foundation/goldens/session-tabs-health-errored.json | 2 +- .../rpc-foundation/goldens/session-tabs-health-reconciled.json | 2 +- mobile/rpc-foundation/goldens/session-tabs-health-refused.json | 2 +- .../goldens/session-tabs-health-stale-application-revision.json | 2 +- .../goldens/session-terminal-display-mode-auto-take-floor.json | 2 +- ...session-terminal-display-mode-auto-without-device-token.json | 2 +- .../session-terminal-display-mode-auto-without-viewport.json | 2 +- .../session-terminal-display-mode-drops-second-toggle.json | 2 +- .../goldens/session-terminal-display-mode-to-desktop.json | 2 +- .../goldens/session-terminal-list-dedupes-handles.json | 2 +- .../goldens/session-terminal-list-empty-guarded.json | 2 +- mobile/rpc-foundation/goldens/session-terminal-list-merged.json | 2 +- .../rpc-foundation/goldens/session-terminal-list-refused.json | 2 +- .../goldens/settings-bot-overrides-fulfilled.json | 2 +- .../goldens/settings-bot-overrides-refresh-refused.json | 2 +- .../rpc-foundation/goldens/settings-bot-overrides-refused.json | 2 +- .../goldens/settings-bot-overrides-transport-error.json | 2 +- mobile/rpc-foundation/goldens/settings-home-coalesced.json | 2 +- .../goldens/settings-home-providers-fulfilled.json | 2 +- .../goldens/settings-home-providers-refuse-after-data.json | 2 +- .../rpc-foundation/goldens/settings-home-providers-refused.json | 2 +- .../goldens/settings-home-providers-transport-error.json | 2 +- mobile/rpc-foundation/goldens/settings-new-tab-refused.json | 2 +- mobile/rpc-foundation/goldens/settings-new-tab-ssh.json | 2 +- .../goldens/settings-new-tab-transport-error.json | 2 +- mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json | 2 +- .../goldens/settings-repo-metadata-fulfilled.json | 2 +- mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json | 2 +- .../goldens/settings-repo-metadata-refuse-after-data.json | 2 +- .../rpc-foundation/goldens/settings-repo-metadata-refused.json | 2 +- .../goldens/settings-repo-metadata-single-host.json | 2 +- .../goldens/settings-repo-metadata-transport-error.json | 2 +- .../goldens/settings-resume-metadata-fulfilled.json | 2 +- .../goldens/settings-resume-metadata-refuse-after-data.json | 2 +- .../goldens/settings-resume-metadata-refused.json | 2 +- .../goldens/settings-resume-metadata-transport-error.json | 2 +- .../goldens/settings-task-hydration-fulfilled.json | 2 +- .../goldens/settings-task-hydration-refuse-after-data.json | 2 +- .../rpc-foundation/goldens/settings-task-hydration-refused.json | 2 +- .../goldens/settings-task-hydration-transport-error.json | 2 +- .../goldens/settings-task-workspace-create-linear.json | 2 +- .../goldens/settings-task-workspace-create-pr-start-point.json | 2 +- .../goldens/settings-task-workspace-fulfilled.json | 2 +- .../rpc-foundation/goldens/settings-task-workspace-refused.json | 2 +- .../goldens/settings-task-workspace-transport-error.json | 2 +- mobile/rpc-foundation/goldens/settings-task-write.json | 2 +- .../goldens/settings-workspace-context-fulfilled.json | 2 +- .../goldens/settings-workspace-context-refuse-after-data.json | 2 +- .../goldens/settings-workspace-context-refused.json | 2 +- .../goldens/settings-workspace-context-transport-error.json | 2 +- .../goldens/settings-workspace-submit-fulfilled.json | 2 +- .../goldens/settings-workspace-submit-refused.json | 2 +- .../goldens/settings-workspace-submit-transport-error.json | 2 +- .../rpc-foundation/goldens/speech-audio-chunk-acknowledged.json | 2 +- .../rpc-foundation/goldens/speech-desktop-start-fulfilled.json | 2 +- .../goldens/speech-desktop-start-recording-failed.json | 2 +- .../rpc-foundation/goldens/speech-desktop-start-superseded.json | 2 +- .../goldens/speech-dictation-session-cancelled.json | 2 +- .../goldens/speech-dictation-session-transcript.json | 2 +- .../goldens/speech-setup-sheet-denied-to-mobile.json | 2 +- mobile/rpc-foundation/goldens/speech-setup-sheet-fulfilled.json | 2 +- .../goldens/speech-setup-sheet-legacy-desktop.json | 2 +- .../goldens/speech-setup-sheet-model-vocabulary.json | 2 +- .../goldens/structured-agent-session-created.json | 2 +- mobile/rpc-foundation/goldens/structured-launch-created.json | 2 +- .../goldens/structured-launch-definitive-refusal.json | 2 +- .../goldens/structured-launch-replays-dropped-create.json | 2 +- .../goldens/structured-launch-support-refused.json | 2 +- .../rpc-foundation/goldens/structured-launch-unsupported.json | 2 +- mobile/rpc-foundation/goldens/tasks-route-repo-list.json | 2 +- .../goldens/terminal-gesture-flush-and-clear.json | 2 +- mobile/rpc-foundation/goldens/terminal-input-send-accepted.json | 2 +- mobile/rpc-foundation/goldens/terminal-input-send-refused.json | 2 +- mobile/rpc-foundation/goldens/terminal-live-input-accepted.json | 2 +- mobile/rpc-foundation/goldens/terminal-paste-accepted.json | 2 +- mobile/rpc-foundation/goldens/terminal-paste-refused.json | 2 +- .../rpc-foundation/goldens/terminal-query-reply-accepted.json | 2 +- .../goldens/terminal-query-reply-unsubscribed.json | 2 +- mobile/rpc-foundation/goldens/terminal-raw-input-refused.json | 2 +- mobile/rpc-foundation/goldens/terminal-raw-input-reported.json | 2 +- .../goldens/terminal-takeover-report-accepted.json | 2 +- .../goldens/terminal-takeover-report-retried.json | 2 +- .../rpc-foundation/goldens/terminal-viewport-refit-applied.json | 2 +- .../goldens/terminal-viewport-refit-legacy-desktop.json | 2 +- .../goldens/terminal-worktree-connection-resolved.json | 2 +- mobile/rpc-foundation/goldens/tk-create-github.json | 2 +- mobile/rpc-foundation/goldens/tk-create-gitlab.json | 2 +- mobile/rpc-foundation/goldens/tk-create-linear.json | 2 +- mobile/rpc-foundation/goldens/tk-item-checks-files.json | 2 +- mobile/rpc-foundation/goldens/tk-item-comment-github.json | 2 +- mobile/rpc-foundation/goldens/tk-item-comment-gitlab-mr.json | 2 +- mobile/rpc-foundation/goldens/tk-item-comment-gitlab.json | 2 +- .../rpc-foundation/goldens/tk-item-detail-github-reactions.json | 2 +- mobile/rpc-foundation/goldens/tk-item-detail-github.json | 2 +- .../rpc-foundation/goldens/tk-item-detail-gitlab-reactions.json | 2 +- mobile/rpc-foundation/goldens/tk-item-detail-gitlab.json | 2 +- mobile/rpc-foundation/goldens/tk-item-detail-linear.json | 2 +- mobile/rpc-foundation/goldens/tk-item-detail-metadata.json | 2 +- mobile/rpc-foundation/goldens/tk-item-merge-gitlab.json | 2 +- mobile/rpc-foundation/goldens/tk-item-metadata-github.json | 2 +- mobile/rpc-foundation/goldens/tk-item-metadata-gitlab-mr.json | 2 +- mobile/rpc-foundation/goldens/tk-item-metadata-gitlab.json | 2 +- mobile/rpc-foundation/goldens/tk-item-reply-merge.json | 2 +- mobile/rpc-foundation/goldens/tk-item-review-github.json | 2 +- mobile/rpc-foundation/goldens/tk-item-status-gitlab-mr.json | 2 +- mobile/rpc-foundation/goldens/tk-item-status-gitlab.json | 2 +- mobile/rpc-foundation/goldens/tk-linear-connect.json | 2 +- mobile/rpc-foundation/goldens/tk-linear-item.json | 2 +- mobile/rpc-foundation/goldens/tk-linear-team-context.json | 2 +- mobile/rpc-foundation/goldens/tk-list-gitlab-items.json | 2 +- mobile/rpc-foundation/goldens/tk-list-gitlab-todos.json | 2 +- mobile/rpc-foundation/goldens/tk-list-linear.json | 2 +- mobile/rpc-foundation/goldens/tk-project-board-load.json | 2 +- mobile/rpc-foundation/goldens/tk-project-repo-slugs.json | 2 +- .../rpc-foundation/goldens/tk-project-row-comments-issue.json | 2 +- mobile/rpc-foundation/goldens/tk-project-row-comments-pr.json | 2 +- mobile/rpc-foundation/goldens/tk-project-row-detail.json | 2 +- mobile/rpc-foundation/goldens/tk-project-row-fields.json | 2 +- mobile/rpc-foundation/goldens/tk-project-row-files-merge.json | 2 +- mobile/rpc-foundation/goldens/tk-project-row-metadata-load.json | 2 +- mobile/rpc-foundation/goldens/tk-project-row-review-checks.json | 2 +- mobile/rpc-foundation/goldens/tk-project-row-threads.json | 2 +- mobile/rpc-foundation/goldens/tk-provider-load.json | 2 +- .../goldens/transport-capability-probe-cutover-reasks-fast.json | 2 +- ...transport-capability-probe-non-string-capabilities-drop.json | 2 +- .../goldens/transport-capability-probe-publishes.json | 2 +- .../goldens/transport-capability-probe-refused-backs-off.json | 2 +- .../transport-host-status-gates-drop-keeps-capabilities.json | 2 +- .../goldens/transport-host-status-gates-ready.json | 2 +- .../goldens/transport-host-status-gates-refused-degrades.json | 2 +- .../goldens/transport-pairing-race-both-refused.json | 2 +- .../goldens/transport-pairing-race-direct-completes-first.json | 2 +- .../goldens/transport-pairing-race-relay-completes-first.json | 2 +- .../transport-pairing-race-relay-wins-when-direct-refused.json | 2 +- mobile/rpc-foundation/goldens/tw-capabilities-advertised.json | 2 +- .../rpc-foundation/goldens/tw-capabilities-cutover-retried.json | 2 +- .../goldens/tw-capabilities-legacy-idempotency.json | 2 +- .../rpc-foundation/goldens/tw-create-retry-agent-launched.json | 2 +- .../goldens/tw-create-retry-ambiguous-after-drop.json | 2 +- .../goldens/tw-create-retry-ambiguous-while-connected.json | 2 +- .../goldens/tw-create-retry-ambiguous-without-idempotency.json | 2 +- mobile/rpc-foundation/goldens/tw-create-retry-created.json | 2 +- .../rpc-foundation/goldens/tw-create-retry-name-collision.json | 2 +- .../goldens/tw-create-retry-unretryable-refusal.json | 2 +- mobile/rpc-foundation/goldens/tw-create-retry-warning-kept.json | 2 +- mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json | 2 +- mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json | 2 +- mobile/rpc-foundation/goldens/tw-paste-lookup-resolved.json | 2 +- mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json | 2 +- .../goldens/tw-paste-lookup-slug-unsupported.json | 2 +- mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json | 2 +- mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json | 2 +- .../rpc-foundation/goldens/tw-smart-search-all-providers.json | 2 +- .../goldens/tw-smart-search-gitlab-provider-error.json | 2 +- .../rpc-foundation/goldens/tw-smart-search-linear-listed.json | 2 +- .../goldens/tw-task-preferences-resume-write.json | 2 +- .../goldens/tw-workspace-source-presets-refused.json | 2 +- mobile/rpc-foundation/goldens/tw-workspace-source-presets.json | 2 +- .../goldens/tw-workspace-sparse-missing-preset.json | 2 +- mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json | 2 +- .../goldens/tw-workspace-ssh-connect-refused.json | 2 +- mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json | 2 +- .../rpc-foundation/goldens/tw-workspace-ssh-local-agents.json | 2 +- mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json | 2 +- .../goldens/worktree-catalog-snapshot-unreadable.json | 2 +- mobile/rpc-foundation/goldens/worktree-catalog-snapshot.json | 2 +- mobile/rpc-foundation/goldens/worktree-home-catalog.json | 2 +- mobile/rpc-foundation/goldens/worktree-retired-names.json | 2 +- mobile/rpc-foundation/pilot-scenarios.json | 2 +- 779 files changed, 779 insertions(+), 779 deletions(-) diff --git a/mobile/rpc-foundation/goldens/aivault-history-scan-fulfilled.json b/mobile/rpc-foundation/goldens/aivault-history-scan-fulfilled.json index 9a29e3dba5b..ee3189f2216 100644 --- a/mobile/rpc-foundation/goldens/aivault-history-scan-fulfilled.json +++ b/mobile/rpc-foundation/goldens/aivault-history-scan-fulfilled.json @@ -3,7 +3,7 @@ "family": "aiVault.history", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7add46922ba5486e56c8acd99a53d083b605f5d98f3cc44ee3cb350ec0406080", diff --git a/mobile/rpc-foundation/goldens/aivault-history-scan-unsupported.json b/mobile/rpc-foundation/goldens/aivault-history-scan-unsupported.json index 6713242ea22..b517233d432 100644 --- a/mobile/rpc-foundation/goldens/aivault-history-scan-unsupported.json +++ b/mobile/rpc-foundation/goldens/aivault-history-scan-unsupported.json @@ -3,7 +3,7 @@ "family": "aiVault.history", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7add46922ba5486e56c8acd99a53d083b605f5d98f3cc44ee3cb350ec0406080", diff --git a/mobile/rpc-foundation/goldens/aivault-history-scan-worktrees-late.json b/mobile/rpc-foundation/goldens/aivault-history-scan-worktrees-late.json index 219d33bd556..56bd6c33543 100644 --- a/mobile/rpc-foundation/goldens/aivault-history-scan-worktrees-late.json +++ b/mobile/rpc-foundation/goldens/aivault-history-scan-worktrees-late.json @@ -3,7 +3,7 @@ "family": "aiVault.history", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7add46922ba5486e56c8acd99a53d083b605f5d98f3cc44ee3cb350ec0406080", diff --git a/mobile/rpc-foundation/goldens/aivault-history-screen-listed.json b/mobile/rpc-foundation/goldens/aivault-history-screen-listed.json index bdd65353a52..3c5c89093ad 100644 --- a/mobile/rpc-foundation/goldens/aivault-history-screen-listed.json +++ b/mobile/rpc-foundation/goldens/aivault-history-screen-listed.json @@ -3,7 +3,7 @@ "family": "aiVault.history-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c78ab47eea594b7e1988403321ff9ba135ca60c513bb9d5848bdde26c0ffe3c3", diff --git a/mobile/rpc-foundation/goldens/aivault-history-screen-worktrees.json b/mobile/rpc-foundation/goldens/aivault-history-screen-worktrees.json index 2e8344adf55..83c59238a20 100644 --- a/mobile/rpc-foundation/goldens/aivault-history-screen-worktrees.json +++ b/mobile/rpc-foundation/goldens/aivault-history-screen-worktrees.json @@ -3,7 +3,7 @@ "family": "aiVault.history-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c78ab47eea594b7e1988403321ff9ba135ca60c513bb9d5848bdde26c0ffe3c3", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-launch-create-refused.json b/mobile/rpc-foundation/goldens/aivault-resume-launch-create-refused.json index 8e84600879e..0808828297a 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-launch-create-refused.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-launch-create-refused.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-launch-invalid-tab.json b/mobile/rpc-foundation/goldens/aivault-resume-launch-invalid-tab.json index 7fbf437c002..80faebdce14 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-launch-invalid-tab.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-launch-invalid-tab.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-launch-locked.json b/mobile/rpc-foundation/goldens/aivault-resume-launch-locked.json index 83ba9f92631..0c08254868a 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-launch-locked.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-launch-locked.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-launch-sent.json b/mobile/rpc-foundation/goldens/aivault-resume-launch-sent.json index 859040920ac..838bafe3ddb 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-launch-sent.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-launch-sent.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-prepare-refused.json b/mobile/rpc-foundation/goldens/aivault-resume-prepare-refused.json index a33a45fdc08..4bd4642be41 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-prepare-refused.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-prepare-refused.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-prepare-repin.json b/mobile/rpc-foundation/goldens/aivault-resume-prepare-repin.json index bfd5aa01e33..0b169324f13 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-prepare-repin.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-prepare-repin.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-prepare-skipped.json b/mobile/rpc-foundation/goldens/aivault-resume-prepare-skipped.json index 0c2fe6c55d0..2d7ccac51e3 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-prepare-skipped.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-prepare-skipped.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/aivault-resume-prepare-unavailable.json b/mobile/rpc-foundation/goldens/aivault-resume-prepare-unavailable.json index 285cc03c20a..7a43e993eb5 100644 --- a/mobile/rpc-foundation/goldens/aivault-resume-prepare-unavailable.json +++ b/mobile/rpc-foundation/goldens/aivault-resume-prepare-unavailable.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/b1.json b/mobile/rpc-foundation/goldens/b1.json index 4b31f46ce10..424f72c16b3 100644 --- a/mobile/rpc-foundation/goldens/b1.json +++ b/mobile/rpc-foundation/goldens/b1.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/b2.json b/mobile/rpc-foundation/goldens/b2.json index 92431367d93..aaddc937a7f 100644 --- a/mobile/rpc-foundation/goldens/b2.json +++ b/mobile/rpc-foundation/goldens/b2.json @@ -3,7 +3,7 @@ "family": "project-explicit-false", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/b3.json b/mobile/rpc-foundation/goldens/b3.json index 0d6f3d2be58..a11a8c18f7b 100644 --- a/mobile/rpc-foundation/goldens/b3.json +++ b/mobile/rpc-foundation/goldens/b3.json @@ -3,7 +3,7 @@ "family": "linear-detail-barrier", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/browser-dialog-accepted.json b/mobile/rpc-foundation/goldens/browser-dialog-accepted.json index fadc8f3dc2d..b9422b20df3 100644 --- a/mobile/rpc-foundation/goldens/browser-dialog-accepted.json +++ b/mobile/rpc-foundation/goldens/browser-dialog-accepted.json @@ -3,7 +3,7 @@ "family": "browser.dialog", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/browser-dialog-dismissed.json b/mobile/rpc-foundation/goldens/browser-dialog-dismissed.json index 170a43574d2..f8a246ba8c8 100644 --- a/mobile/rpc-foundation/goldens/browser-dialog-dismissed.json +++ b/mobile/rpc-foundation/goldens/browser-dialog-dismissed.json @@ -3,7 +3,7 @@ "family": "browser.dialog", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/browser-keyboard-input.json b/mobile/rpc-foundation/goldens/browser-keyboard-input.json index 65bc5709394..5812dae044d 100644 --- a/mobile/rpc-foundation/goldens/browser-keyboard-input.json +++ b/mobile/rpc-foundation/goldens/browser-keyboard-input.json @@ -3,7 +3,7 @@ "family": "browser.keyboard", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/browser-pointer-click-accepted.json b/mobile/rpc-foundation/goldens/browser-pointer-click-accepted.json index 2fb934ac18c..15ce598798e 100644 --- a/mobile/rpc-foundation/goldens/browser-pointer-click-accepted.json +++ b/mobile/rpc-foundation/goldens/browser-pointer-click-accepted.json @@ -3,7 +3,7 @@ "family": "browser.pointer-click", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/browser-pointer-click-fallback.json b/mobile/rpc-foundation/goldens/browser-pointer-click-fallback.json index e3953d5078c..be0b8c92452 100644 --- a/mobile/rpc-foundation/goldens/browser-pointer-click-fallback.json +++ b/mobile/rpc-foundation/goldens/browser-pointer-click-fallback.json @@ -3,7 +3,7 @@ "family": "browser.pointer-click", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/browser-wheel-scrolled.json b/mobile/rpc-foundation/goldens/browser-wheel-scrolled.json index 2118fe8274b..783db0a2a6e 100644 --- a/mobile/rpc-foundation/goldens/browser-wheel-scrolled.json +++ b/mobile/rpc-foundation/goldens/browser-wheel-scrolled.json @@ -3,7 +3,7 @@ "family": "browser.wheel", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-attachment-anonymous.json b/mobile/rpc-foundation/goldens/clipboard-image-attachment-anonymous.json index 39d26f72c18..7ccc43bcfe0 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-attachment-anonymous.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-attachment-anonymous.json @@ -3,7 +3,7 @@ "family": "clipboard.image-attachment", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-attachment-blocked-before-send.json b/mobile/rpc-foundation/goldens/clipboard-image-attachment-blocked-before-send.json index fdd29e642fb..3bea239bfab 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-attachment-blocked-before-send.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-attachment-blocked-before-send.json @@ -3,7 +3,7 @@ "family": "clipboard.image-attachment", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-attachment-cancelled.json b/mobile/rpc-foundation/goldens/clipboard-image-attachment-cancelled.json index 060e98a0cd5..01b4e22c3e4 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-attachment-cancelled.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-attachment-cancelled.json @@ -3,7 +3,7 @@ "family": "clipboard.image-attachment", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-attachment-pasted.json b/mobile/rpc-foundation/goldens/clipboard-image-attachment-pasted.json index a7608a934cf..23184e8aa59 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-attachment-pasted.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-attachment-pasted.json @@ -3,7 +3,7 @@ "family": "clipboard.image-attachment", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-attachment-upload-refused.json b/mobile/rpc-foundation/goldens/clipboard-image-attachment-upload-refused.json index d5d370accc3..34fba5c6308 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-attachment-upload-refused.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-attachment-upload-refused.json @@ -3,7 +3,7 @@ "family": "clipboard.image-attachment", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-upload-aborts-on-chunk-failure.json b/mobile/rpc-foundation/goldens/clipboard-image-upload-aborts-on-chunk-failure.json index f0ddb7f1646..86b73c156fc 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-upload-aborts-on-chunk-failure.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-upload-aborts-on-chunk-failure.json @@ -3,7 +3,7 @@ "family": "clipboard.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-upload-chunked.json b/mobile/rpc-foundation/goldens/clipboard-image-upload-chunked.json index fbb47aa7e9b..b69a45cda87 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-upload-chunked.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-upload-chunked.json @@ -3,7 +3,7 @@ "family": "clipboard.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-upload-single-frame-fallback.json b/mobile/rpc-foundation/goldens/clipboard-image-upload-single-frame-fallback.json index 689725d00fb..2a03971b35b 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-upload-single-frame-fallback.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-upload-single-frame-fallback.json @@ -3,7 +3,7 @@ "family": "clipboard.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/clipboard-image-upload-start-refused.json b/mobile/rpc-foundation/goldens/clipboard-image-upload-start-refused.json index 5c2fc25301f..25d136c95ad 100644 --- a/mobile/rpc-foundation/goldens/clipboard-image-upload-start-refused.json +++ b/mobile/rpc-foundation/goldens/clipboard-image-upload-start-refused.json @@ -3,7 +3,7 @@ "family": "clipboard.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/codex-reset-credit-consumed.json b/mobile/rpc-foundation/goldens/codex-reset-credit-consumed.json index d44a3a46cd4..16d13527d53 100644 --- a/mobile/rpc-foundation/goldens/codex-reset-credit-consumed.json +++ b/mobile/rpc-foundation/goldens/codex-reset-credit-consumed.json @@ -3,7 +3,7 @@ "family": "components.codex-reset-credit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "76b53dea504a688493843e23e8e7d052fc196f559bb1e2579d0fd38bf3156d0e", diff --git a/mobile/rpc-foundation/goldens/codex-reset-credit-resumed.json b/mobile/rpc-foundation/goldens/codex-reset-credit-resumed.json index 0041e246aa7..67ffe01d128 100644 --- a/mobile/rpc-foundation/goldens/codex-reset-credit-resumed.json +++ b/mobile/rpc-foundation/goldens/codex-reset-credit-resumed.json @@ -3,7 +3,7 @@ "family": "components.codex-reset-credit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "76b53dea504a688493843e23e8e7d052fc196f559bb1e2579d0fd38bf3156d0e", diff --git a/mobile/rpc-foundation/goldens/components-codex-capability.json b/mobile/rpc-foundation/goldens/components-codex-capability.json index 0a671ae86d7..13211875513 100644 --- a/mobile/rpc-foundation/goldens/components-codex-capability.json +++ b/mobile/rpc-foundation/goldens/components-codex-capability.json @@ -3,7 +3,7 @@ "family": "components.codex-reset-capability", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/components-setup-ask.json b/mobile/rpc-foundation/goldens/components-setup-ask.json index 901a2b44a31..bfb612a8164 100644 --- a/mobile/rpc-foundation/goldens/components-setup-ask.json +++ b/mobile/rpc-foundation/goldens/components-setup-ask.json @@ -3,7 +3,7 @@ "family": "components.setup-script", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/components-target-local.json b/mobile/rpc-foundation/goldens/components-target-local.json index 74d97414930..a4052d7ef4f 100644 --- a/mobile/rpc-foundation/goldens/components-target-local.json +++ b/mobile/rpc-foundation/goldens/components-target-local.json @@ -3,7 +3,7 @@ "family": "components.execution-target-local", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/components-target-ssh.json b/mobile/rpc-foundation/goldens/components-target-ssh.json index 1f581e9b244..83581b3af71 100644 --- a/mobile/rpc-foundation/goldens/components-target-ssh.json +++ b/mobile/rpc-foundation/goldens/components-target-ssh.json @@ -3,7 +3,7 @@ "family": "components.execution-target", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/diff-review-branch-compare.json b/mobile/rpc-foundation/goldens/diff-review-branch-compare.json index 98723436c85..dbbd4a250a8 100644 --- a/mobile/rpc-foundation/goldens/diff-review-branch-compare.json +++ b/mobile/rpc-foundation/goldens/diff-review-branch-compare.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/diff-review-branch-file-diff.json b/mobile/rpc-foundation/goldens/diff-review-branch-file-diff.json index f2721d14600..72d68f80f5e 100644 --- a/mobile/rpc-foundation/goldens/diff-review-branch-file-diff.json +++ b/mobile/rpc-foundation/goldens/diff-review-branch-file-diff.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/diff-review-notes-refused-before-compare.json b/mobile/rpc-foundation/goldens/diff-review-notes-refused-before-compare.json index 9b229f7e6dd..db90689dc6f 100644 --- a/mobile/rpc-foundation/goldens/diff-review-notes-refused-before-compare.json +++ b/mobile/rpc-foundation/goldens/diff-review-notes-refused-before-compare.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/diff-review-refused-file-diff.json b/mobile/rpc-foundation/goldens/diff-review-refused-file-diff.json index 56095e4a63e..37f4dafd853 100644 --- a/mobile/rpc-foundation/goldens/diff-review-refused-file-diff.json +++ b/mobile/rpc-foundation/goldens/diff-review-refused-file-diff.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/diff-review-snapshot.json b/mobile/rpc-foundation/goldens/diff-review-snapshot.json index 57ab3b50be0..4d566df590b 100644 --- a/mobile/rpc-foundation/goldens/diff-review-snapshot.json +++ b/mobile/rpc-foundation/goldens/diff-review-snapshot.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/diff-review-status-unavailable.json b/mobile/rpc-foundation/goldens/diff-review-status-unavailable.json index 8d7b5fabb67..f1275b88b4d 100644 --- a/mobile/rpc-foundation/goldens/diff-review-status-unavailable.json +++ b/mobile/rpc-foundation/goldens/diff-review-status-unavailable.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/diff-review-worktree-file-diff.json b/mobile/rpc-foundation/goldens/diff-review-worktree-file-diff.json index 8fcd52dcc00..6586c18c453 100644 --- a/mobile/rpc-foundation/goldens/diff-review-worktree-file-diff.json +++ b/mobile/rpc-foundation/goldens/diff-review-worktree-file-diff.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/file-tap-open-refused.json b/mobile/rpc-foundation/goldens/file-tap-open-refused.json index 7dcbb6b64a9..7cf29b9a7f2 100644 --- a/mobile/rpc-foundation/goldens/file-tap-open-refused.json +++ b/mobile/rpc-foundation/goldens/file-tap-open-refused.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/file-tap-opens-worktree-file.json b/mobile/rpc-foundation/goldens/file-tap-opens-worktree-file.json index 50b84efd16f..6f5cab9af62 100644 --- a/mobile/rpc-foundation/goldens/file-tap-opens-worktree-file.json +++ b/mobile/rpc-foundation/goldens/file-tap-opens-worktree-file.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/file-tap-previews-absolute-artifact.json b/mobile/rpc-foundation/goldens/file-tap-previews-absolute-artifact.json index 9c1f4275db1..f029affc780 100644 --- a/mobile/rpc-foundation/goldens/file-tap-previews-absolute-artifact.json +++ b/mobile/rpc-foundation/goldens/file-tap-previews-absolute-artifact.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/file-tap-resolve-miss.json b/mobile/rpc-foundation/goldens/file-tap-resolve-miss.json index a854453e156..d2f90369eb0 100644 --- a/mobile/rpc-foundation/goldens/file-tap-resolve-miss.json +++ b/mobile/rpc-foundation/goldens/file-tap-resolve-miss.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/file-tap-resolve-refused.json b/mobile/rpc-foundation/goldens/file-tap-resolve-refused.json index af42db14f77..b74a5018ded 100644 --- a/mobile/rpc-foundation/goldens/file-tap-resolve-refused.json +++ b/mobile/rpc-foundation/goldens/file-tap-resolve-refused.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/files-explorer-legacy-fallback.json b/mobile/rpc-foundation/goldens/files-explorer-legacy-fallback.json index 6e60676bd49..cea0f5c91e0 100644 --- a/mobile/rpc-foundation/goldens/files-explorer-legacy-fallback.json +++ b/mobile/rpc-foundation/goldens/files-explorer-legacy-fallback.json @@ -3,7 +3,7 @@ "family": "files.explorer-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7a42a348f4407b94cf75ac77a4b6b783b7d28103b1ae1d111d2708dab0dd4a0c", diff --git a/mobile/rpc-foundation/goldens/files-explorer-readdir.json b/mobile/rpc-foundation/goldens/files-explorer-readdir.json index ebc75852848..84669eee874 100644 --- a/mobile/rpc-foundation/goldens/files-explorer-readdir.json +++ b/mobile/rpc-foundation/goldens/files-explorer-readdir.json @@ -3,7 +3,7 @@ "family": "files.explorer-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7a42a348f4407b94cf75ac77a4b6b783b7d28103b1ae1d111d2708dab0dd4a0c", diff --git a/mobile/rpc-foundation/goldens/files-ownership-local.json b/mobile/rpc-foundation/goldens/files-ownership-local.json index c77d585bf75..c1b2a2b89ff 100644 --- a/mobile/rpc-foundation/goldens/files-ownership-local.json +++ b/mobile/rpc-foundation/goldens/files-ownership-local.json @@ -3,7 +3,7 @@ "family": "files.mutation-ownership", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-ownership-ssh.json b/mobile/rpc-foundation/goldens/files-ownership-ssh.json index 14649405161..aea3fd487dc 100644 --- a/mobile/rpc-foundation/goldens/files-ownership-ssh.json +++ b/mobile/rpc-foundation/goldens/files-ownership-ssh.json @@ -3,7 +3,7 @@ "family": "files.mutation-ownership", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-artifact-direct.json b/mobile/rpc-foundation/goldens/files-preview-artifact-direct.json index a5c4b4f7936..3541aa7ccb4 100644 --- a/mobile/rpc-foundation/goldens/files-preview-artifact-direct.json +++ b/mobile/rpc-foundation/goldens/files-preview-artifact-direct.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-artifact-image-read.json b/mobile/rpc-foundation/goldens/files-preview-artifact-image-read.json index a5ff1cfdeb4..b5f0e107b3f 100644 --- a/mobile/rpc-foundation/goldens/files-preview-artifact-image-read.json +++ b/mobile/rpc-foundation/goldens/files-preview-artifact-image-read.json @@ -3,7 +3,7 @@ "family": "files.preview-artifact-image", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-artifact-image.json b/mobile/rpc-foundation/goldens/files-preview-artifact-image.json index 812372d4b3d..74aa49f9220 100644 --- a/mobile/rpc-foundation/goldens/files-preview-artifact-image.json +++ b/mobile/rpc-foundation/goldens/files-preview-artifact-image.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-grant-refresh.json b/mobile/rpc-foundation/goldens/files-preview-grant-refresh.json index 0ecf16d4782..dd57ba203de 100644 --- a/mobile/rpc-foundation/goldens/files-preview-grant-refresh.json +++ b/mobile/rpc-foundation/goldens/files-preview-grant-refresh.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-worktree-image-read.json b/mobile/rpc-foundation/goldens/files-preview-worktree-image-read.json index 4c92b29c631..8d16cefba74 100644 --- a/mobile/rpc-foundation/goldens/files-preview-worktree-image-read.json +++ b/mobile/rpc-foundation/goldens/files-preview-worktree-image-read.json @@ -3,7 +3,7 @@ "family": "files.preview-worktree-image", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-worktree-image.json b/mobile/rpc-foundation/goldens/files-preview-worktree-image.json index 5f9df391ae8..a3472a3c4a5 100644 --- a/mobile/rpc-foundation/goldens/files-preview-worktree-image.json +++ b/mobile/rpc-foundation/goldens/files-preview-worktree-image.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-worktree-text-read.json b/mobile/rpc-foundation/goldens/files-preview-worktree-text-read.json index 62768b8c8aa..7c427d89318 100644 --- a/mobile/rpc-foundation/goldens/files-preview-worktree-text-read.json +++ b/mobile/rpc-foundation/goldens/files-preview-worktree-text-read.json @@ -3,7 +3,7 @@ "family": "files.preview-worktree-text", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-preview-worktree.json b/mobile/rpc-foundation/goldens/files-preview-worktree.json index 1c5dc8e052e..1d642951eb4 100644 --- a/mobile/rpc-foundation/goldens/files-preview-worktree.json +++ b/mobile/rpc-foundation/goldens/files-preview-worktree.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-save-blind.json b/mobile/rpc-foundation/goldens/files-save-blind.json index a344040d975..e3e07a2b881 100644 --- a/mobile/rpc-foundation/goldens/files-save-blind.json +++ b/mobile/rpc-foundation/goldens/files-save-blind.json @@ -3,7 +3,7 @@ "family": "files.preview-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-save-verified.json b/mobile/rpc-foundation/goldens/files-save-verified.json index 66a8317c631..a9ab9936cf2 100644 --- a/mobile/rpc-foundation/goldens/files-save-verified.json +++ b/mobile/rpc-foundation/goldens/files-save-verified.json @@ -3,7 +3,7 @@ "family": "files.preview-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/files-tab-doc-shapes.json b/mobile/rpc-foundation/goldens/files-tab-doc-shapes.json index 90567841aca..704f63849e6 100644 --- a/mobile/rpc-foundation/goldens/files-tab-doc-shapes.json +++ b/mobile/rpc-foundation/goldens/files-tab-doc-shapes.json @@ -3,7 +3,7 @@ "family": "files.tab-doc", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/home-host-accounts.json b/mobile/rpc-foundation/goldens/home-host-accounts.json index ec72f151127..0b1dc5777a1 100644 --- a/mobile/rpc-foundation/goldens/home-host-accounts.json +++ b/mobile/rpc-foundation/goldens/home-host-accounts.json @@ -3,7 +3,7 @@ "family": "home.host-accounts", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c632fdbc4b730777ecb09f08bec14cca0586042b01ed99d40d0228806c7def4a", diff --git a/mobile/rpc-foundation/goldens/home-host-stats.json b/mobile/rpc-foundation/goldens/home-host-stats.json index 9e7db3c70f1..524d1ab36b2 100644 --- a/mobile/rpc-foundation/goldens/home-host-stats.json +++ b/mobile/rpc-foundation/goldens/home-host-stats.json @@ -3,7 +3,7 @@ "family": "home.host-stats", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a9e0780298a1443664e7ae02056168aa34d67556c9c056d51a82c7b4a73ad35b", diff --git a/mobile/rpc-foundation/goldens/host-view-settings-sync.json b/mobile/rpc-foundation/goldens/host-view-settings-sync.json index 15288aa2ae4..7565fd9cbc3 100644 --- a/mobile/rpc-foundation/goldens/host-view-settings-sync.json +++ b/mobile/rpc-foundation/goldens/host-view-settings-sync.json @@ -3,7 +3,7 @@ "family": "host.view-settings", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a9e0780298a1443664e7ae02056168aa34d67556c9c056d51a82c7b4a73ad35b", diff --git a/mobile/rpc-foundation/goldens/host-worktree-actions-pin-open-delete.json b/mobile/rpc-foundation/goldens/host-worktree-actions-pin-open-delete.json index dc5c84a2aa9..83ed50c9a95 100644 --- a/mobile/rpc-foundation/goldens/host-worktree-actions-pin-open-delete.json +++ b/mobile/rpc-foundation/goldens/host-worktree-actions-pin-open-delete.json @@ -3,7 +3,7 @@ "family": "host.worktree-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "92c29bd78ca0c0d5917e9386fc447bb9a1698b1d1ffaba0db7546eaac60da639", diff --git a/mobile/rpc-foundation/goldens/host-worktree-delete-refused.json b/mobile/rpc-foundation/goldens/host-worktree-delete-refused.json index 12f83c121e3..73c0867bb0d 100644 --- a/mobile/rpc-foundation/goldens/host-worktree-delete-refused.json +++ b/mobile/rpc-foundation/goldens/host-worktree-delete-refused.json @@ -3,7 +3,7 @@ "family": "host.worktree-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "92c29bd78ca0c0d5917e9386fc447bb9a1698b1d1ffaba0db7546eaac60da639", diff --git a/mobile/rpc-foundation/goldens/host-worktree-refresh-stream.json b/mobile/rpc-foundation/goldens/host-worktree-refresh-stream.json index ce404e225cc..3c9c1434487 100644 --- a/mobile/rpc-foundation/goldens/host-worktree-refresh-stream.json +++ b/mobile/rpc-foundation/goldens/host-worktree-refresh-stream.json @@ -3,7 +3,7 @@ "family": "host-worktree-refresh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.json b/mobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.json index 7ed8fd34921..853310eb8b2 100644 --- a/mobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.json +++ b/mobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.json b/mobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.json index 89322e5507b..2c8c813bf43 100644 --- a/mobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.json +++ b/mobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/inventory-lifecycle.json b/mobile/rpc-foundation/goldens/inventory-lifecycle.json index ffd9220457f..f34ecb55237 100644 --- a/mobile/rpc-foundation/goldens/inventory-lifecycle.json +++ b/mobile/rpc-foundation/goldens/inventory-lifecycle.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/inventory-repeat-query.json b/mobile/rpc-foundation/goldens/inventory-repeat-query.json index af2eee99e52..c1719dc71c9 100644 --- a/mobile/rpc-foundation/goldens/inventory-repeat-query.json +++ b/mobile/rpc-foundation/goldens/inventory-repeat-query.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/lifecycle-b3.json b/mobile/rpc-foundation/goldens/lifecycle-b3.json index 1146603393f..3df3403dd81 100644 --- a/mobile/rpc-foundation/goldens/lifecycle-b3.json +++ b/mobile/rpc-foundation/goldens/lifecycle-b3.json @@ -3,7 +3,7 @@ "family": "linear-detail-barrier", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.json b/mobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.json index 67113a20bb6..738ac1d76d7 100644 --- a/mobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.json +++ b/mobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.json b/mobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.json index 993915d8c1d..e13d93bc00f 100644 --- a/mobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.json +++ b/mobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json b/mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json index 5912fb5c06b..344911618f1 100644 --- a/mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json +++ b/mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json b/mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json index eee35dbaa4e..b92956af95c 100644 --- a/mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json +++ b/mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/linear-select-workspace.json b/mobile/rpc-foundation/goldens/linear-select-workspace.json index 90fdefa4091..c05e32cb10f 100644 --- a/mobile/rpc-foundation/goldens/linear-select-workspace.json +++ b/mobile/rpc-foundation/goldens/linear-select-workspace.json @@ -3,7 +3,7 @@ "family": "linear.select-workspace-picker", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b65996c152b632d553a31e07e31ea5c76f998eada42cf0966923d730da21908f", diff --git a/mobile/rpc-foundation/goldens/live-worktree-name-stream.json b/mobile/rpc-foundation/goldens/live-worktree-name-stream.json index ac5006123d2..13eb852db99 100644 --- a/mobile/rpc-foundation/goldens/live-worktree-name-stream.json +++ b/mobile/rpc-foundation/goldens/live-worktree-name-stream.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.create-1.json b/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.create-1.json index 3b85818d93b..70e9cd3a5c0 100644 --- a/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.create-1.json +++ b/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.create-1.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.createsupport-1.json b/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.createsupport-1.json index 108850abfb8..0e2a209ad15 100644 --- a/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.createsupport-1.json +++ b/mobile/rpc-foundation/goldens/matrix-agentsession.structured-create-agentsession.createsupport-1.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/matrix-agentsession.structured-launch-agentsession.createsupport-1.json b/mobile/rpc-foundation/goldens/matrix-agentsession.structured-launch-agentsession.createsupport-1.json index 2279cacba82..4d5b49f379a 100644 --- a/mobile/rpc-foundation/goldens/matrix-agentsession.structured-launch-agentsession.createsupport-1.json +++ b/mobile/rpc-foundation/goldens/matrix-agentsession.structured-launch-agentsession.createsupport-1.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.history-aivault.listsessions-1.json b/mobile/rpc-foundation/goldens/matrix-aivault.history-aivault.listsessions-1.json index 12a459c5145..f555965c67a 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.history-aivault.listsessions-1.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.history-aivault.listsessions-1.json @@ -3,7 +3,7 @@ "family": "aiVault.history", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7add46922ba5486e56c8acd99a53d083b605f5d98f3cc44ee3cb350ec0406080", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-platform-status.json b/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-platform-status.json index a6779db6004..959c2f6ab7c 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-platform-status.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-platform-status.json @@ -3,7 +3,7 @@ "family": "aiVault.history-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c78ab47eea594b7e1988403321ff9ba135ca60c513bb9d5848bdde26c0ffe3c3", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-status.get-2.json b/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-status.get-2.json index 30d685dab7b..a9a79e25f57 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-status.get-2.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-status.get-2.json @@ -3,7 +3,7 @@ "family": "aiVault.history-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c78ab47eea594b7e1988403321ff9ba135ca60c513bb9d5848bdde26c0ffe3c3", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-worktree.ps-1.json b/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-worktree.ps-1.json index eb9d19065e7..32ae31e6ce8 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-worktree.ps-1.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.history-screen-worktree.ps-1.json @@ -3,7 +3,7 @@ "family": "aiVault.history-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c78ab47eea594b7e1988403321ff9ba135ca60c513bb9d5848bdde26c0ffe3c3", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.history-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-aivault.history-status.get-1.json index f4e7407b43e..bd9253c7c0d 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.history-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.history-status.get-1.json @@ -3,7 +3,7 @@ "family": "aiVault.history", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7add46922ba5486e56c8acd99a53d083b605f5d98f3cc44ee3cb350ec0406080", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-session.tabs.createterminal-1.json b/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-session.tabs.createterminal-1.json index 2d72bf6a740..50ea47be769 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-session.tabs.createterminal-1.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-session.tabs.createterminal-1.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-terminal.send-1.json index 1df683e2c0e..6cdef2e7283 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.resume-launch-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/matrix-aivault.resume-preparation-aivault.preparesessionresume-1.json b/mobile/rpc-foundation/goldens/matrix-aivault.resume-preparation-aivault.preparesessionresume-1.json index 92d31138743..8ef05104d57 100644 --- a/mobile/rpc-foundation/goldens/matrix-aivault.resume-preparation-aivault.preparesessionresume-1.json +++ b/mobile/rpc-foundation/goldens/matrix-aivault.resume-preparation-aivault.preparesessionresume-1.json @@ -3,7 +3,7 @@ "family": "aiVault.resume-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2f43211e4084c0493bd02ec98868acf53a4c748f89a70cd9657c9c3ee87b12fb", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.dialog-browser.dialogaccept-1.json b/mobile/rpc-foundation/goldens/matrix-browser.dialog-browser.dialogaccept-1.json index 535d1f208bc..006be6ad564 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.dialog-browser.dialogaccept-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.dialog-browser.dialogaccept-1.json @@ -3,7 +3,7 @@ "family": "browser.dialog", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keyboardinserttext-1.json b/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keyboardinserttext-1.json index cb3be08420c..2dbd58984b4 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keyboardinserttext-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keyboardinserttext-1.json @@ -3,7 +3,7 @@ "family": "browser.keyboard", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keypress-1.json b/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keypress-1.json index b7674cc2b41..078648f615f 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keypress-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.keyboard-browser.keypress-1.json @@ -3,7 +3,7 @@ "family": "browser.keyboard", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseclick-1.json b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseclick-1.json index e6b80a491fa..9e47d02bddb 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseclick-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseclick-1.json @@ -3,7 +3,7 @@ "family": "browser.pointer-click", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousedown-1.json b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousedown-1.json index 2c4157c7088..0df087718f9 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousedown-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousedown-1.json @@ -3,7 +3,7 @@ "family": "browser.pointer-click", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousemove-1.json b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousemove-1.json index 0ea7fcd6039..fbdcd63f4da 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousemove-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mousemove-1.json @@ -3,7 +3,7 @@ "family": "browser.pointer-click", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseup-1.json b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseup-1.json index 5dbdd5db6b0..56e9ae62833 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseup-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.pointer-click-browser.mouseup-1.json @@ -3,7 +3,7 @@ "family": "browser.pointer-click", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousemove-1.json b/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousemove-1.json index 597e94ca599..b0811e193a6 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousemove-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousemove-1.json @@ -3,7 +3,7 @@ "family": "browser.wheel", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousewheel-1.json b/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousewheel-1.json index 4fb0e89f803..939488d91a9 100644 --- a/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousewheel-1.json +++ b/mobile/rpc-foundation/goldens/matrix-browser.wheel-browser.mousewheel-1.json @@ -3,7 +3,7 @@ "family": "browser.wheel", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55971752f963202d30160851197a301089b0f3ebd0c46725af1a461d8310d658", diff --git a/mobile/rpc-foundation/goldens/matrix-clipboard.image-attachment-clipboard.startimageupload-1.json b/mobile/rpc-foundation/goldens/matrix-clipboard.image-attachment-clipboard.startimageupload-1.json index 6cb0ef464b4..a93ce6407e2 100644 --- a/mobile/rpc-foundation/goldens/matrix-clipboard.image-attachment-clipboard.startimageupload-1.json +++ b/mobile/rpc-foundation/goldens/matrix-clipboard.image-attachment-clipboard.startimageupload-1.json @@ -3,7 +3,7 @@ "family": "clipboard.image-attachment", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.saveimageastempfile-1.json b/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.saveimageastempfile-1.json index d7236f70362..4a554ca2aa2 100644 --- a/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.saveimageastempfile-1.json +++ b/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.saveimageastempfile-1.json @@ -3,7 +3,7 @@ "family": "clipboard.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.startimageupload-1.json b/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.startimageupload-1.json index ae64b7f5008..1a19f87851b 100644 --- a/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.startimageupload-1.json +++ b/mobile/rpc-foundation/goldens/matrix-clipboard.image-upload-clipboard.startimageupload-1.json @@ -3,7 +3,7 @@ "family": "clipboard.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/matrix-components.codex-reset-capability-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-components.codex-reset-capability-status.get-1.json index 1c80c13ef55..78dc2c550d2 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.codex-reset-capability-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.codex-reset-capability-status.get-1.json @@ -3,7 +3,7 @@ "family": "components.codex-reset-capability", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/matrix-components.codex-reset-credit-accounts.consumecodexresetcredit-1.json b/mobile/rpc-foundation/goldens/matrix-components.codex-reset-credit-accounts.consumecodexresetcredit-1.json index 6a21aa1d2aa..69a4d5612ab 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.codex-reset-credit-accounts.consumecodexresetcredit-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.codex-reset-credit-accounts.consumecodexresetcredit-1.json @@ -3,7 +3,7 @@ "family": "components.codex-reset-credit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "76b53dea504a688493843e23e8e7d052fc196f559bb1e2579d0fd38bf3156d0e", diff --git a/mobile/rpc-foundation/goldens/matrix-components.execution-target-local-preflight.detectagents-1.json b/mobile/rpc-foundation/goldens/matrix-components.execution-target-local-preflight.detectagents-1.json index f1d2016855e..1a8d82eefc7 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.execution-target-local-preflight.detectagents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.execution-target-local-preflight.detectagents-1.json @@ -3,7 +3,7 @@ "family": "components.execution-target-local", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/matrix-components.execution-target-preflight.detectremoteagents-1.json b/mobile/rpc-foundation/goldens/matrix-components.execution-target-preflight.detectremoteagents-1.json index 0505d3630c9..4ef5502e028 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.execution-target-preflight.detectremoteagents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.execution-target-preflight.detectremoteagents-1.json @@ -3,7 +3,7 @@ "family": "components.execution-target", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.connect-1.json b/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.connect-1.json index 84cceeb1e0c..9e7ee19ac54 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.connect-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.connect-1.json @@ -3,7 +3,7 @@ "family": "components.execution-target", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.getstate-1.json b/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.getstate-1.json index 6c03eea2d46..5e1362ee1a2 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.getstate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.execution-target-ssh.getstate-1.json @@ -3,7 +3,7 @@ "family": "components.execution-target", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/matrix-components.new-workspace-repositories-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-components.new-workspace-repositories-repo.list-1.json index d36c2a9e7a4..db08cb0a1cf 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.new-workspace-repositories-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.new-workspace-repositories-repo.list-1.json @@ -3,7 +3,7 @@ "family": "components.new-workspace-repositories", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "64c1772f0f95a3c43fbb14398a8804b2b4784f7f18874e4fd79767ae634c7faa", diff --git a/mobile/rpc-foundation/goldens/matrix-components.setup-script-repo.hooks-1.json b/mobile/rpc-foundation/goldens/matrix-components.setup-script-repo.hooks-1.json index e3062cb7b08..4e69f077ce1 100644 --- a/mobile/rpc-foundation/goldens/matrix-components.setup-script-repo.hooks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-components.setup-script-repo.hooks-1.json @@ -3,7 +3,7 @@ "family": "components.setup-script", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5cfbce3c7d97d908fbd447646d611e41a8aa1f61f684b9b710c4b67d6ff023a7", diff --git a/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.list-1.json b/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.list-1.json index 31dca92626b..eca6a71d703 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.list-1.json @@ -3,7 +3,7 @@ "family": "files.explorer-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7a42a348f4407b94cf75ac77a4b6b783b7d28103b1ae1d111d2708dab0dd4a0c", diff --git a/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.readdir-1.json b/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.readdir-1.json index faa67607c2d..1da89da354e 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.readdir-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.explorer-screen-files.readdir-1.json @@ -3,7 +3,7 @@ "family": "files.explorer-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7a42a348f4407b94cf75ac77a4b6b783b7d28103b1ae1d111d2708dab0dd4a0c", diff --git a/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-ssh.getstate-1.json b/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-ssh.getstate-1.json index 72618fcd072..5be34b18290 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-ssh.getstate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-ssh.getstate-1.json @@ -3,7 +3,7 @@ "family": "files.mutation-ownership", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-status.get-1.json index c4a05864640..3f5b4de815c 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-status.get-1.json @@ -3,7 +3,7 @@ "family": "files.mutation-ownership", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-worktree.show-1.json index fa6f67a8530..05380376292 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.mutation-ownership-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "files.mutation-ownership", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-artifact-image-files.readterminalartifactpreview-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-artifact-image-files.readterminalartifactpreview-1.json index 70c6c8afe0c..5ec96e49f04 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-artifact-image-files.readterminalartifactpreview-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-artifact-image-files.readterminalartifactpreview-1.json @@ -3,7 +3,7 @@ "family": "files.preview-artifact-image", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-1.json index ca399e3363f..971f35d057b 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-1.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-2.json b/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-2.json index cbe16c69333..237069c4cd5 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-2.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.readterminalartifact-2.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.resolveterminalpath-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.resolveterminalpath-1.json index bf801aaa41e..1424a7b1067 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.resolveterminalpath-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-load-files.resolveterminalpath-1.json @@ -3,7 +3,7 @@ "family": "files.preview-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.readterminalartifact-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.readterminalartifact-1.json index 7c80759ea10..3c2c830bff2 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.readterminalartifact-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.readterminalartifact-1.json @@ -3,7 +3,7 @@ "family": "files.preview-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.writeterminalartifact-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.writeterminalartifact-1.json index 3da5920cac5..7de1dcc0f63 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.writeterminalartifact-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-save-files.writeterminalartifact-1.json @@ -3,7 +3,7 @@ "family": "files.preview-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-image-files.readpreview-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-image-files.readpreview-1.json index 651514e738c..365dd8d34ef 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-image-files.readpreview-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-image-files.readpreview-1.json @@ -3,7 +3,7 @@ "family": "files.preview-worktree-image", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-text-files.read-1.json b/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-text-files.read-1.json index 7afaf630fb4..a18c1b98594 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-text-files.read-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.preview-worktree-text-files.read-1.json @@ -3,7 +3,7 @@ "family": "files.preview-worktree-text", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.read-1.json b/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.read-1.json index 0d1ecd509e5..1da1adef54c 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.read-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.read-1.json @@ -3,7 +3,7 @@ "family": "files.tab-doc", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.readpreview-1.json b/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.readpreview-1.json index 8ad27276644..1736e6e9def 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.readpreview-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.tab-doc-files.readpreview-1.json @@ -3,7 +3,7 @@ "family": "files.tab-doc", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.tab-doc-git.diff-1.json b/mobile/rpc-foundation/goldens/matrix-files.tab-doc-git.diff-1.json index 430a2fcd3fd..6821ae4aa11 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.tab-doc-git.diff-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.tab-doc-git.diff-1.json @@ -3,7 +3,7 @@ "family": "files.tab-doc", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b93fc8b6f1ece041acaa8cc5699852085e67d7e2f39db13145e483f854ea3309", diff --git a/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.open-1.json b/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.open-1.json index 6ef3799be3d..bdf424f3fa1 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.open-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.open-1.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.resolveterminalpath-1.json b/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.resolveterminalpath-1.json index 0552e0397c8..31d3005d1b1 100644 --- a/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.resolveterminalpath-1.json +++ b/mobile/rpc-foundation/goldens/matrix-files.terminal-path-tap-files.resolveterminalpath-1.json @@ -3,7 +3,7 @@ "family": "files.terminal-path-tap", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e20a76ecd5e820dc4797fb25307810af68b5ced996dba2b9599464a21b5cbe1b", diff --git a/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.json b/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.json index 8f8984e68ff..4699d770255 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.json index 37d2b8373a1..9bc438eb63b 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json index 8959874ae8a..b02c66ed162 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-git.branch-diff-preview-git.branchdiff-1.json b/mobile/rpc-foundation/goldens/matrix-git.branch-diff-preview-git.branchdiff-1.json index 422c9e18ed7..091ca528d4a 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.branch-diff-preview-git.branchdiff-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.branch-diff-preview-git.branchdiff-1.json @@ -3,7 +3,7 @@ "family": "git.branch-diff-preview", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.branchcompare-1.json b/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.branchcompare-1.json index 42fabf5168c..350cdbed16a 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.branchcompare-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.branchcompare-1.json @@ -3,7 +3,7 @@ "family": "git.changes-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.status-1.json b/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.status-1.json index a6ee1a50c1f..caa43333149 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.changes-load-git.status-1.json @@ -3,7 +3,7 @@ "family": "git.changes-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.changes-load-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-git.changes-load-repo.list-1.json index 09239788301..ff02f789c5d 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.changes-load-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.changes-load-repo.list-1.json @@ -3,7 +3,7 @@ "family": "git.changes-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.changes-load-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-git.changes-load-worktree.show-1.json index b66e74626f7..7ac5b6b6913 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.changes-load-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.changes-load-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "git.changes-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json b/mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json index f23aa91d930..74b1fb9f1d8 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json @@ -3,7 +3,7 @@ "family": "git.commit-message-ai", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.commitcompare-1.json b/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.commitcompare-1.json index fa029a9e6aa..ace479c2f18 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.commitcompare-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.commitcompare-1.json @@ -3,7 +3,7 @@ "family": "git.history-commit-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.history-1.json b/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.history-1.json index 590ef4a9b3b..5f7dbda538d 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.history-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.history-commit-files-git.history-1.json @@ -3,7 +3,7 @@ "family": "git.history-commit-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json b/mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json index 777b01c76c8..c359bdd3bb4 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json @@ -3,7 +3,7 @@ "family": "git.history-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.json b/mobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.json index 6aeea3a843d..f566344209b 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.json @@ -3,7 +3,7 @@ "family": "git.remote-prerequisite", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.json b/mobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.json index a2927ab6c9a..e6358429049 100644 --- a/mobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addissuecomment-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addissuecomment-1.json index 15e03d314c7..b5323514e47 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addissuecomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addissuecomment-1.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addprreviewcommentreply-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addprreviewcommentreply-1.json index 18d5661310f..9833d78fbc6 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addprreviewcommentreply-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.addprreviewcommentreply-1.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.deleteissuecommentbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.deleteissuecommentbyslug-1.json index 5897217930b..62c0dd35043 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.deleteissuecommentbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.deleteissuecommentbyslug-1.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.updateissuecommentbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.updateissuecommentbyslug-1.json index c95201e1542..4d6688102a4 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.updateissuecommentbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.project.updateissuecommentbyslug-1.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.resolvereviewthread-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.resolvereviewthread-1.json index 22ab073e5d4..aee0b2f0e33 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.resolvereviewthread-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-comment-mutation-github.resolvereviewthread-1.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.mergepr-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.mergepr-1.json index d90e325a31f..c58ff8b756b 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.mergepr-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.mergepr-1.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.removeprreviewers-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.removeprreviewers-1.json index 3b6e83fa3b1..b395601f085 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.removeprreviewers-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.removeprreviewers-1.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.requestprreviewers-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.requestprreviewers-1.json index d60594a638d..0460fcc08a9 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.requestprreviewers-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.requestprreviewers-1.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.rerunprchecks-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.rerunprchecks-1.json index 448ad27cc1f..d6c67c17c12 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.rerunprchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.rerunprchecks-1.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.setprautomerge-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.setprautomerge-1.json index 0d198da942e..9c8a420c3d2 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.setprautomerge-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.setprautomerge-1.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.updateprstate-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.updateprstate-1.json index 071f78ecd2a..7afdb86a91a 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.updateprstate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-mutation-github.updateprstate-1.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.listassignableusers-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.listassignableusers-1.json index 97d1fca12df..d905e9d39a3 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.listassignableusers-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.listassignableusers-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prcheckdetails-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prcheckdetails-1.json index 33314251451..1392b519efd 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prcheckdetails-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prcheckdetails-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prchecks-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prchecks-1.json index 4bf5e12b743..b4d000f621d 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prchecks-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prforbranch-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prforbranch-1.json index ae97ece0e97..23d074f7713 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prforbranch-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.prforbranch-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.reposlug-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.reposlug-1.json index fb1190ffc3c..df658791386 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.reposlug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.reposlug-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.workitemdetails-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.workitemdetails-1.json index e02c1d6a41c..e0628c7b1d1 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.workitemdetails-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-github.workitemdetails-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-read-hostedreview.forbranch-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-read-hostedreview.forbranch-1.json index 2f4d99e39ff..ff82a429a31 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-read-hostedreview.forbranch-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-read-hostedreview.forbranch-1.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-github.pr-title-mutation-github.updateprtitle-1.json b/mobile/rpc-foundation/goldens/matrix-github.pr-title-mutation-github.updateprtitle-1.json index e5baef40307..80afc8eedf0 100644 --- a/mobile/rpc-foundation/goldens/matrix-github.pr-title-mutation-github.updateprtitle-1.json +++ b/mobile/rpc-foundation/goldens/matrix-github.pr-title-mutation-github.updateprtitle-1.json @@ -3,7 +3,7 @@ "family": "github.pr-title-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-home.host-accounts-accounts.list-1.json b/mobile/rpc-foundation/goldens/matrix-home.host-accounts-accounts.list-1.json index d0c02061fae..bde0e6d333c 100644 --- a/mobile/rpc-foundation/goldens/matrix-home.host-accounts-accounts.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-home.host-accounts-accounts.list-1.json @@ -3,7 +3,7 @@ "family": "home.host-accounts", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c632fdbc4b730777ecb09f08bec14cca0586042b01ed99d40d0228806c7def4a", diff --git a/mobile/rpc-foundation/goldens/matrix-home.host-stats-stats.summary-1.json b/mobile/rpc-foundation/goldens/matrix-home.host-stats-stats.summary-1.json index cca6825e42b..ae04f1d198d 100644 --- a/mobile/rpc-foundation/goldens/matrix-home.host-stats-stats.summary-1.json +++ b/mobile/rpc-foundation/goldens/matrix-home.host-stats-stats.summary-1.json @@ -3,7 +3,7 @@ "family": "home.host-stats", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a9e0780298a1443664e7ae02056168aa34d67556c9c056d51a82c7b4a73ad35b", diff --git a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-1.json b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-1.json index 02a9358f688..34a07d4547a 100644 --- a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-1.json @@ -3,7 +3,7 @@ "family": "host-worktree-refresh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-2.json b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-2.json index aecb353baf4..81506b70b9e 100644 --- a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-2.json +++ b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-2.json @@ -3,7 +3,7 @@ "family": "host-worktree-refresh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-3.json b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-3.json index b8cf54759ac..6d5c1da6a05 100644 --- a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-3.json +++ b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-1-3.json @@ -3,7 +3,7 @@ "family": "host-worktree-refresh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-2-1.json b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-2-1.json index 931052948a6..ef748abed79 100644 --- a/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-2-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host-worktree-refresh-runtime.clientevents.subscribe-2-1.json @@ -3,7 +3,7 @@ "family": "host-worktree-refresh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.get-1.json b/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.get-1.json index 9db62cad8cf..166a84f198d 100644 --- a/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.get-1.json @@ -3,7 +3,7 @@ "family": "host.view-settings", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a9e0780298a1443664e7ae02056168aa34d67556c9c056d51a82c7b4a73ad35b", diff --git a/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.set-1.json b/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.set-1.json index 3908471ac32..f303df5b630 100644 --- a/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host.view-settings-ui.set-1.json @@ -3,7 +3,7 @@ "family": "host.view-settings", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a9e0780298a1443664e7ae02056168aa34d67556c9c056d51a82c7b4a73ad35b", diff --git a/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.activate-1.json b/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.activate-1.json index 062d53e65fc..fc19a28e2d4 100644 --- a/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.activate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.activate-1.json @@ -3,7 +3,7 @@ "family": "host.worktree-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "92c29bd78ca0c0d5917e9386fc447bb9a1698b1d1ffaba0db7546eaac60da639", diff --git a/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.rm-1.json b/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.rm-1.json index 5ca3f3a321b..20fd2976f47 100644 --- a/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.rm-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.rm-1.json @@ -3,7 +3,7 @@ "family": "host.worktree-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "92c29bd78ca0c0d5917e9386fc447bb9a1698b1d1ffaba0db7546eaac60da639", diff --git a/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.set-1.json b/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.set-1.json index d24ad24deec..ef8d89d1b64 100644 --- a/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-host.worktree-actions-worktree.set-1.json @@ -3,7 +3,7 @@ "family": "host.worktree-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "92c29bd78ca0c0d5917e9386fc447bb9a1698b1d1ffaba0db7546eaac60da639", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.json index 7339d2b28ec..76496ea77a8 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.json index 64d3e2870eb..d61b23c1a07 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json index 22b4253f74e..6e685e963d5 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.json index 37d90f497f0..1a87319774b 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.json index 35ade29c97e..93a76304c07 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json index dc7a8e48217..a1b222a118b 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json index 706c34e0b3d..5c1baa3051f 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.json index 75e8b253acd..ad0b0f5a551 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.json index 79aa8cacae7..3a2346b2ae0 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.json index d68bae9f3d4..4f5608befb9 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.json index 93c39dc0f20..0b2ed8b9b6f 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json index 886bd5e50b9..3b367478bcd 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json index 47d504c743a..f2e02ad33b5 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.json index d785e21db8c..9493e1620ad 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.json index 2482c3f92bc..fbb4e6fafdc 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.json b/mobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.json index 4cbafa01954..2ef8f40373d 100644 --- a/mobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.json +++ b/mobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.json @@ -3,7 +3,7 @@ "family": "hostedReview.eligibility", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.json b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.json index 7656477d1ab..b644d6f0dc2 100644 --- a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.json +++ b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.json b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.json index 99f0beb11de..2b837f98190 100644 --- a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.json +++ b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json index 2b194734dbb..f11079a518e 100644 --- a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json +++ b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json index ae6ab4958ad..2ab4cf73a6a 100644 --- a/mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json +++ b/mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json @@ -3,7 +3,7 @@ "family": "legacy-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "262eaad263a45aa13ec5b27c12b59946b12c202474229fff7a5727dba6d702ca", diff --git a/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json b/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json index 1aacddc5929..e37363ded8d 100644 --- a/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json @@ -3,7 +3,7 @@ "family": "linear-detail-barrier", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.json b/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.json index b1cb622ffc7..cdfa4c40e1f 100644 --- a/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.json +++ b/mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.json @@ -3,7 +3,7 @@ "family": "linear-detail-barrier", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/matrix-linear.select-workspace-picker-linear.selectworkspace-1.json b/mobile/rpc-foundation/goldens/matrix-linear.select-workspace-picker-linear.selectworkspace-1.json index fd970967ec8..35773fef987 100644 --- a/mobile/rpc-foundation/goldens/matrix-linear.select-workspace-picker-linear.selectworkspace-1.json +++ b/mobile/rpc-foundation/goldens/matrix-linear.select-workspace-picker-linear.selectworkspace-1.json @@ -3,7 +3,7 @@ "family": "linear.select-workspace-picker", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b65996c152b632d553a31e07e31ea5c76f998eada42cf0966923d730da21908f", diff --git a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-1.json b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-1.json index c482e0c9358..9a98a01a615 100644 --- a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-1.json +++ b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-1.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-2.json b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-2.json index 4ac97e05f1f..448f33ba903 100644 --- a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-2.json +++ b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-1-2.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-2-1.json b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-2-1.json index 78bbf0e52e6..b991fea43be 100644 --- a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-2-1.json +++ b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-runtime.clientevents.subscribe-2-1.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-1.json index 53667dca0fe..f10ec0f01cf 100644 --- a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-2.json b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-2.json index 7579662ab25..1b64409e34d 100644 --- a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-2.json +++ b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-2.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-3.json b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-3.json index 39c54f5e65b..4471240376d 100644 --- a/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-3.json +++ b/mobile/rpc-foundation/goldens/matrix-live-worktree-name-worktree.show-3.json @@ -3,7 +3,7 @@ "family": "live-worktree-name", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8e41c8624b9b6185e447cee3590a851ab88b6b1ee1d632af90e3a771a90310be", diff --git a/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-1.json index 429db450e4a..c660212b892 100644 --- a/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-2.json b/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-2.json index 9a3b673ee82..2408059a3de 100644 --- a/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-2.json +++ b/mobile/rpc-foundation/goldens/matrix-nativechat.image-paste-terminal.send-2.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/matrix-nativechat.image-upload-clipboard.startimageupload-1.json b/mobile/rpc-foundation/goldens/matrix-nativechat.image-upload-clipboard.startimageupload-1.json index 9a878191a87..6bcc68ae4e0 100644 --- a/mobile/rpc-foundation/goldens/matrix-nativechat.image-upload-clipboard.startimageupload-1.json +++ b/mobile/rpc-foundation/goldens/matrix-nativechat.image-upload-clipboard.startimageupload-1.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/matrix-nativechat.session-option-pick-settings.mutatenativechatsessionoptions-1.json b/mobile/rpc-foundation/goldens/matrix-nativechat.session-option-pick-settings.mutatenativechatsessionoptions-1.json index d33b37c2e4a..d929a9ef4c8 100644 --- a/mobile/rpc-foundation/goldens/matrix-nativechat.session-option-pick-settings.mutatenativechatsessionoptions-1.json +++ b/mobile/rpc-foundation/goldens/matrix-nativechat.session-option-pick-settings.mutatenativechatsessionoptions-1.json @@ -3,7 +3,7 @@ "family": "nativeChat.session-option-pick", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-orchestration.workerterminaluserinput-1.json index ec53d971955..0fd9dcb63fb 100644 --- a/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-terminal.send-1.json index d501347b7a6..79d9c60d60c 100644 --- a/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-nativechat.terminal-write-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.getmissedsince-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.getmissedsince-1.json index 2805c82accc..6689bf37e41 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.getmissedsince-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.getmissedsince-1.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-1.json index 202f36e9a8b..c9da82f05f7 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-1.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-2.json b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-2.json index beb936438e7..e44bb6f2e78 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-2.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.subscribe-1-2.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.unsubscribe-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.unsubscribe-1.json index 4d9e729b0be..dd286c55ebb 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.unsubscribe-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.desktop-stream-notifications.unsubscribe-1.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.display-test-screen-notifications.testpush-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.display-test-screen-notifications.testpush-1.json index f02050c95d8..171a2085f4a 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.display-test-screen-notifications.testpush-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.display-test-screen-notifications.testpush-1.json @@ -3,7 +3,7 @@ "family": "notifications.display-test-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "12a04986290715d5db23a1eb5192c1138bb2d375d1b1dd37ba310ea89eb11566", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.push-dismissal-notifications.getmissedsince-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.push-dismissal-notifications.getmissedsince-1.json index 854b254b142..5973d0e1d7e 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.push-dismissal-notifications.getmissedsince-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.push-dismissal-notifications.getmissedsince-1.json @@ -3,7 +3,7 @@ "family": "notifications.push-dismissal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "595a3eb2994d0596b9fcd0707b175b4e978625053dfbc5c541b0350c0cbfb524", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.registerpush-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.registerpush-1.json index 0fb3537564a..c79adaa0255 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.registerpush-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.registerpush-1.json @@ -3,7 +3,7 @@ "family": "notifications.push-registration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2e3d939dc162dbc5a38d8a7207111688204a825fd70348721917b3016e1c9470", diff --git a/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.unregisterpush-1.json b/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.unregisterpush-1.json index c164f461ddf..c58dcc2a5bb 100644 --- a/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.unregisterpush-1.json +++ b/mobile/rpc-foundation/goldens/matrix-notifications.push-registration-notifications.unregisterpush-1.json @@ -3,7 +3,7 @@ "family": "notifications.push-registration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2e3d939dc162dbc5a38d8a7207111688204a825fd70348721917b3016e1c9470", diff --git a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.json b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.json index 4d1bc79aba4..c804c127297 100644 --- a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.json +++ b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.json b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.json index b2c66dc648f..0aa43d5ac1e 100644 --- a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.json +++ b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.json b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.json index 79ac33df7eb..d77be27ac25 100644 --- a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.json +++ b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.json b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.json index 70cf49ca8e0..597905dd562 100644 --- a/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.json +++ b/mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json b/mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json index 420e695b6b1..9e9ccbd4636 100644 --- a/mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json @@ -3,7 +3,7 @@ "family": "project-explicit-false", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.json b/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.json index c23efdb0485..feed9049c1f 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.json @@ -3,7 +3,7 @@ "family": "relay.credential-rotation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.json b/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.json index b1252da9c68..55ff37fdb72 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.json @@ -3,7 +3,7 @@ "family": "relay.credential-rotation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.json b/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.json index 2eb48b76687..e9d546ccc3d 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.json @@ -3,7 +3,7 @@ "family": "relay.credential-rotation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.json b/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.json index dc1b7267bb4..5eb4b2f1606 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.json @@ -3,7 +3,7 @@ "family": "relay.direct-upgrade", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.json b/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.json index 9898c3eb5dd..fd4a564d8e4 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.json @@ -3,7 +3,7 @@ "family": "relay.direct-upgrade", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.json b/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.json index 49a68b22d67..9558cebba6c 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.json @@ -3,7 +3,7 @@ "family": "relay.direct-upgrade", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.json b/mobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.json index 59e5cd4546a..e172e779b0c 100644 --- a/mobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.json +++ b/mobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.json @@ -3,7 +3,7 @@ "family": "relay.pairing-recovery", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/matrix-session.browser-tab-create-browser.tabcreate-1.json b/mobile/rpc-foundation/goldens/matrix-session.browser-tab-create-browser.tabcreate-1.json index 94db8134b5a..7937d423ee9 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.browser-tab-create-browser.tabcreate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.browser-tab-create-browser.tabcreate-1.json @@ -3,7 +3,7 @@ "family": "session.browser-tab-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.content-create-files.createfile-1.json b/mobile/rpc-foundation/goldens/matrix-session.content-create-files.createfile-1.json index c4da9bf755e..d3adf585c3d 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.content-create-files.createfile-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.content-create-files.createfile-1.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.content-create-files.open-1.json b/mobile/rpc-foundation/goldens/matrix-session.content-create-files.open-1.json index 7422ba0b968..e29e2155b0c 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.content-create-files.open-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.content-create-files.open-1.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.content-create-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-session.content-create-status.get-1.json index 957a3381923..85346ef670f 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.content-create-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.content-create-status.get-1.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.content-create-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-session.content-create-worktree.show-1.json index 36476e7f9ea..223c0188bad 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.content-create-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.content-create-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.create-terminal-session.tabs.createterminal-1.json b/mobile/rpc-foundation/goldens/matrix-session.create-terminal-session.tabs.createterminal-1.json index e9e89946a84..43bd417113e 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.create-terminal-session.tabs.createterminal-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.create-terminal-session.tabs.createterminal-1.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/matrix-session.create-terminal-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-session.create-terminal-terminal.send-1.json index 331a733f381..d35868af1f1 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.create-terminal-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.create-terminal-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-notes-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-session.diff-notes-worktree.show-1.json index 354427126aa..528342e6f45 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-notes-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-notes-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "session.diff-notes", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-review-actions-worktree.set-1.json b/mobile/rpc-foundation/goldens/matrix-session.diff-review-actions-worktree.set-1.json index 242eb523e1a..a9092d652fa 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-review-actions-worktree.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-review-actions-worktree.set-1.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-review-base-ref-show.json b/mobile/rpc-foundation/goldens/matrix-session.diff-review-base-ref-show.json index 72d08868efe..aacd6049e55 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-review-base-ref-show.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-review-base-ref-show.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.branchcompare-1.json b/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.branchcompare-1.json index 6259a7ceed8..e40ae4fff9a 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.branchcompare-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.branchcompare-1.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.status-1.json b/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.status-1.json index 436e1eda4d3..15bece53a2d 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-review-git.status-1.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-review-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.diff-review-repo.list-1.json index 5f8b892ab79..9f33618b3ed 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-review-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-review-repo.list-1.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.diff-review-review-show.json b/mobile/rpc-foundation/goldens/matrix-session.diff-review-review-show.json index bebd001e2dc..47cc5cc5173 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.diff-review-review-show.json +++ b/mobile/rpc-foundation/goldens/matrix-session.diff-review-review-show.json @@ -3,7 +3,7 @@ "family": "session.diff-review", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-files.read-1.json b/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-files.read-1.json index d8024c77990..28f0f997ed4 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-files.read-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-files.read-1.json @@ -3,7 +3,7 @@ "family": "session.markdown-disk-fallback", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-markdown.readtab-1.json b/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-markdown.readtab-1.json index 8c9d60ceca9..ab5e7722e4f 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-markdown.readtab-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.markdown-disk-fallback-markdown.readtab-1.json @@ -3,7 +3,7 @@ "family": "session.markdown-disk-fallback", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.markdown-save-markdown.savetab-1.json b/mobile/rpc-foundation/goldens/matrix-session.markdown-save-markdown.savetab-1.json index 06d12b655e2..a16f19ae733 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.markdown-save-markdown.savetab-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.markdown-save-markdown.savetab-1.json @@ -3,7 +3,7 @@ "family": "session.markdown-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.readsession-1.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.readsession-1.json index a1df6833bac..5b04ae97049 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.readsession-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.readsession-1.json @@ -3,7 +3,7 @@ "family": "session.native-chat-page", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "60ce67f66134d6385aa7fb47f2135436a7b3ce0221e3b2514bea65e06dba5518", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-1-1.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-1-1.json index 3961b81f776..72511dc9e9b 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-1-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-1-1.json @@ -3,7 +3,7 @@ "family": "session.native-chat-page", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "60ce67f66134d6385aa7fb47f2135436a7b3ce0221e3b2514bea65e06dba5518", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-2-1.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-2-1.json index de1821727c7..8f8f6f5ad03 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-2-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-page-nativechat.subscribe-2-1.json @@ -3,7 +3,7 @@ "family": "session.native-chat-page", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "60ce67f66134d6385aa7fb47f2135436a7b3ce0221e3b2514bea65e06dba5518", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-readability-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-readability-repo.list-1.json index 2dabb391413..24b26b23e4a 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-readability-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-readability-repo.list-1.json @@ -3,7 +3,7 @@ "family": "session.native-chat-readability", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-orchestration.workerterminaluserinput-1.json index 9e536ca4313..5310701e356 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "session.native-chat-stop", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-1.json index e13eca87bc2..54b2fe96822 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "session.native-chat-stop", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-2.json b/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-2.json index e13ed7a96bf..5925663b025 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-2.json +++ b/mobile/rpc-foundation/goldens/matrix-session.native-chat-stop-terminal.send-2.json @@ -3,7 +3,7 @@ "family": "session.native-chat-stop", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.branchcompare-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.branchcompare-1.json index d0777ecd384..ebca2119c20 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.branchcompare-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.branchcompare-1.json @@ -3,7 +3,7 @@ "family": "session.pr-branch-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.status-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.status-1.json index 15ced76c11f..1ecf9606299 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-git.status-1.json @@ -3,7 +3,7 @@ "family": "session.pr-branch-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-repo.list-1.json index 578ba42698b..49c0a004034 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-repo.list-1.json @@ -3,7 +3,7 @@ "family": "session.pr-branch-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-worktree.show-1.json index 03670230aeb..4d05d74a774 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-branch-context-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "session.pr-branch-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prchecks-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prchecks-1.json index 282fa0ea25f..ddaa1535764 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prchecks-1.json @@ -3,7 +3,7 @@ "family": "session.pr-sidebar", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "87ffa2daea415d2682f1112025b5bd9d52404c6fc7c239180a3ad2120678ff1f", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prforbranch-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prforbranch-1.json index ee8ac79095c..e61a24812e7 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prforbranch-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-github.prforbranch-1.json @@ -3,7 +3,7 @@ "family": "session.pr-sidebar", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "87ffa2daea415d2682f1112025b5bd9d52404c6fc7c239180a3ad2120678ff1f", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-hostedreview.forbranch-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-hostedreview.forbranch-1.json index bfaf822a9cf..32b7b02a6dc 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-hostedreview.forbranch-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-hostedreview.forbranch-1.json @@ -3,7 +3,7 @@ "family": "session.pr-sidebar", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "87ffa2daea415d2682f1112025b5bd9d52404c6fc7c239180a3ad2120678ff1f", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-worktree.show-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-worktree.show-1.json index 43b106cb08f..ee7e575771e 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-worktree.show-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-sidebar-worktree.show-1.json @@ -3,7 +3,7 @@ "family": "session.pr-sidebar", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "87ffa2daea415d2682f1112025b5bd9d52404c6fc7c239180a3ad2120678ff1f", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-triage-session.tabs.createterminal-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-triage-session.tabs.createterminal-1.json index 578f6f5cd75..d199564d62c 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-triage-session.tabs.createterminal-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-triage-session.tabs.createterminal-1.json @@ -3,7 +3,7 @@ "family": "session.pr-triage", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-session.pr-triage-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-session.pr-triage-terminal.send-1.json index 1da0ae482ae..7d2bf88d581 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.pr-triage-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.pr-triage-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "session.pr-triage", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-branch-diff-git.branchdiff-1.json b/mobile/rpc-foundation/goldens/matrix-session.review-branch-diff-git.branchdiff-1.json index 35574c74f2f..61badbcb9d0 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-branch-diff-git.branchdiff-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-branch-diff-git.branchdiff-1.json @@ -3,7 +3,7 @@ "family": "session.review-branch-diff", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-1.json b/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-1.json index cd2b5091286..c807c216852 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-1.json @@ -3,7 +3,7 @@ "family": "session.review-file-diff", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-2.json b/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-2.json index df4117078aa..a797d08f740 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-2.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-2.json @@ -3,7 +3,7 @@ "family": "session.review-file-diff", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-3.json b/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-3.json index d3204f9059e..6f6b580715e 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-3.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-file-diff-git.diff-3.json @@ -3,7 +3,7 @@ "family": "session.review-file-diff", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.discard-1.json b/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.discard-1.json index 8bad7c0e511..f5fd3188d83 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.discard-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.discard-1.json @@ -3,7 +3,7 @@ "family": "session.review-git-mutations", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-1.json b/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-1.json index 41fcfd189da..26d33eb5d90 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-1.json @@ -3,7 +3,7 @@ "family": "session.review-git-mutations", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-2.json b/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-2.json index 46fcfac32e0..caef72ecad2 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-2.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-git-mutations-git.stage-2.json @@ -3,7 +3,7 @@ "family": "session.review-git-mutations", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/matrix-session.review-send-sheet-session.tabs.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.review-send-sheet-session.tabs.list-1.json index 3880f546d96..7000bef1980 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.review-send-sheet-session.tabs.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.review-send-sheet-session.tabs.list-1.json @@ -3,7 +3,7 @@ "family": "session.review-send-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-1.json b/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-1.json index 83a29e78e31..c562aa4f903 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-1.json @@ -3,7 +3,7 @@ "family": "session.startup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "6b08c394e37fd572cf63a4c11934247d379008f11117aa5af33b2926fbd32d1e", diff --git a/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-2.json b/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-2.json index 9438ee47df8..e510ceebcd3 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-2.json +++ b/mobile/rpc-foundation/goldens/matrix-session.startup-worktree.activate-2.json @@ -3,7 +3,7 @@ "family": "session.startup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "6b08c394e37fd572cf63a4c11934247d379008f11117aa5af33b2926fbd32d1e", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-activation-session.tabs.activate-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-activation-session.tabs.activate-1.json index 71710e101dc..d138fb695a3 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-activation-session.tabs.activate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-activation-session.tabs.activate-1.json @@ -3,7 +3,7 @@ "family": "session.tab-activation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-activation-terminal.focus-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-activation-terminal.focus-1.json index c4b9ec2d898..f5d150d9972 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-activation-terminal.focus-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-activation-terminal.focus-1.json @@ -3,7 +3,7 @@ "family": "session.tab-activation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-close-session-session.tabs.close-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-close-session-session.tabs.close-1.json index b5d27cb7c73..3f019077e7b 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-close-session-session.tabs.close-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-close-session-session.tabs.close-1.json @@ -3,7 +3,7 @@ "family": "session.tab-close-session", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-close-terminal.close-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-close-terminal.close-1.json index 192fb7703df..db353bbcfa9 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-close-terminal.close-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-close-terminal.close-1.json @@ -3,7 +3,7 @@ "family": "session.tab-close", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-documents-markdown.readtab-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-documents-markdown.readtab-1.json index f7f47f2adc1..932571e6155 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-documents-markdown.readtab-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-documents-markdown.readtab-1.json @@ -3,7 +3,7 @@ "family": "session.tab-documents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-rename-terminal.rename-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-rename-terminal.rename-1.json index 03c1a3e309f..58b3884daa4 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-rename-terminal.rename-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-rename-terminal.rename-1.json @@ -3,7 +3,7 @@ "family": "session.tab-rename", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.json index c4b7c4f4afa..e4cf277de9b 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.json @@ -3,7 +3,7 @@ "family": "session.tab-reveal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json index e5a21fa55b9..2aa1e03d6f3 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json @@ -3,7 +3,7 @@ "family": "session.tab-reveal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-session.tabs-stream-health-session.tabs.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.tabs-stream-health-session.tabs.list-1.json index 49aef0cdf6b..b35e403bfae 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.tabs-stream-health-session.tabs.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.tabs-stream-health-session.tabs.list-1.json @@ -3,7 +3,7 @@ "family": "session.tabs-stream-health", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-display-mode-terminal.setdisplaymode-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-display-mode-terminal.setdisplaymode-1.json index 0b93c0c540e..9e29ad3fcfe 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-display-mode-terminal.setdisplaymode-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-display-mode-terminal.setdisplaymode-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-display-mode", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9e90ad39a8d4257adf30166757a6364c4710dc3ae9f06365f80a3dd8f1c94d89", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-orchestration.workerterminaluserinput-1.json index 11011527344..d6dd86e5125 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-gesture-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9d119d5ec320e2538105d6ff673b9f4b8e3decbe46527947489dffbcf2ac0472", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.clearbuffer-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.clearbuffer-1.json index 6992251b6e7..e3e27458724 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.clearbuffer-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.clearbuffer-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-gesture-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9d119d5ec320e2538105d6ff673b9f4b8e3decbe46527947489dffbcf2ac0472", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.send-1.json index 5316724ae6c..6fdd6c6e3c1 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-gesture-input-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-gesture-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9d119d5ec320e2538105d6ff673b9f4b8e3decbe46527947489dffbcf2ac0472", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-orchestration.workerterminaluserinput-1.json index b07a08c2416..209707eb204 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-input-send", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-terminal.send-1.json index ba0dc00f52a..31c2c258b4e 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-input-send-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-input-send", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-inventory-terminal.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-inventory-terminal.list-1.json index e129307c92f..2485b957e56 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-inventory-terminal.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-inventory-terminal.list-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-orchestration.workerterminaluserinput-1.json index 68b070b95eb..ee23e454a0f 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-settings.get-1.json index a032008296e..d888d9d8e97 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-settings.get-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-terminal.send-1.json index 0c0fc586054..a374c85ed9e 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.terminal-paste-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "session.terminal-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-repo.list-1.json index 07a34f934a8..3a4cc2a668c 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-repo.list-1.json @@ -3,7 +3,7 @@ "family": "session.worktree-connection", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-settings.get-1.json index ee3c1cc8904..e731af4eded 100644 --- a/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-session.worktree-connection-settings.get-1.json @@ -3,7 +3,7 @@ "family": "session.worktree-connection", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.json b/mobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.json index fc3b3438521..bd8fe086517 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json index a19986021b8..0c3f2ed82e7 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json index b17427b5f7e..79cb2602334 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json b/mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json index b0cc7bf5b5c..9141433148f 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json @@ -3,7 +3,7 @@ "family": "settings-best-effort", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.json index 3919f2304a3..e4d2ec19192 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.json b/mobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.json index 4e30db8316f..b7c68d25145 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.json b/mobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.json index 0a6d55a3d58..cc1107cc032 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json index dbd1035e79f..c8a1f547cb4 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-preflight.detectagents-1.json b/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-preflight.detectagents-1.json index 8689147909e..06a5d830069 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-preflight.detectagents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-preflight.detectagents-1.json @@ -3,7 +3,7 @@ "family": "settings.new-tab-local-agents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-repo.list-1.json index 7ba74819cc0..be297103994 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-repo.list-1.json @@ -3,7 +3,7 @@ "family": "settings.new-tab-local-agents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-settings.get-1.json index 2b73de8ed70..9efdf7628d9 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.new-tab-local-agents-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.new-tab-local-agents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.getterminalquickcommands-1.json b/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.getterminalquickcommands-1.json index 3b49e16a03d..970b1954702 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.getterminalquickcommands-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.getterminalquickcommands-1.json @@ -3,7 +3,7 @@ "family": "settings.quick-commands", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.updateterminalquickcommands-1.json b/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.updateterminalquickcommands-1.json index f7c4f78ff01..2777711cbef 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.updateterminalquickcommands-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.quick-commands-settings.updateterminalquickcommands-1.json @@ -3,7 +3,7 @@ "family": "settings.quick-commands", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json index f1563a76dcc..1c552208819 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json index e2c256ff3b2..d4a67cf6721 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json index c74c8d2ba0e..bdcdd23aa91 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json index 7a6e306f68c..c6398578bbb 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.json index 91bd512d960..efdabb52dc8 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.json index f219ba10b70..b7ab972ef14 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.json index bf4a1d412bc..a352d673dd4 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.json index 7a4d3819f94..2f99dc6913e 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.json b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.json index c0b36902d53..317164e097a 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.json index bae5449a266..809cac0cb0f 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.json index 2d257800a8a..27a97870acc 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.json index 758f2b88c0f..1a36e67804b 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.json index ea17a7ddba1..15e04f42c4a 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json index b2fa11193c1..282aa51dd34 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json index ef57b8c85f1..e7729076fdb 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.json index f262c63337b..25c39f1c4d2 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json index 3e3be53a599..1529b12da3e 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.json b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.json index 80f72b2c64d..c1384aeefd1 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.json b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.json index 1972517cb1c..333402f476d 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json index db5c291a0b0..d96bc82e8fc 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json index 89e3ef19d3a..49a2f3599eb 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json index 6ce514e00b5..016a3c123c1 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json @@ -3,7 +3,7 @@ "family": "settings.workspace-submit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.dictation-chunk-speech.dictation.chunk-1.json b/mobile/rpc-foundation/goldens/matrix-speech.dictation-chunk-speech.dictation.chunk-1.json index 02c5dfc261c..c09d52dbdb1 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.dictation-chunk-speech.dictation.chunk-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.dictation-chunk-speech.dictation.chunk-1.json @@ -3,7 +3,7 @@ "family": "speech.dictation-chunk", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.finish-1.json b/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.finish-1.json index cfb93b27d7a..4424aa41f05 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.finish-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.finish-1.json @@ -3,7 +3,7 @@ "family": "speech.dictation-session", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.start-1.json b/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.start-1.json index 13b16dbe91c..cb8e2eb4f03 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.start-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.dictation-session-speech.dictation.start-1.json @@ -3,7 +3,7 @@ "family": "speech.dictation-session", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.cancel-1.json b/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.cancel-1.json index ce8aea374fe..ae47f95d589 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.cancel-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.cancel-1.json @@ -3,7 +3,7 @@ "family": "speech.dictation-start", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.start-1.json b/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.start-1.json index ba5767f0abe..e772c23079c 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.start-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.dictation-start-speech.dictation.start-1.json @@ -3,7 +3,7 @@ "family": "speech.dictation-start", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.dictation.setup-1.json b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.dictation.setup-1.json index 98662e41971..ce3e047cc6c 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.dictation.setup-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.dictation.setup-1.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.delete-1.json b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.delete-1.json index ec67a796fc7..a990fcc3c9c 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.delete-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.delete-1.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.download-1.json b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.download-1.json index 5139cbc2cbc..9f76b612d1f 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.download-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.download-1.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.list-1.json b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.list-1.json index c398c955455..c725717e4fd 100644 --- a/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-speech.setup-sheet-speech.models.list-1.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.addprreviewcomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.addprreviewcomment-1.json index 39997d41bfe..969adfb7bd3 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.addprreviewcomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.addprreviewcomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-checks-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.prfilecontents-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.prfilecontents-1.json index f31867024f8..a90b60cd162 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.prfilecontents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.prfilecontents-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-checks-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.rerunprchecks-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.rerunprchecks-1.json index 8243f2251d1..37abeda39f5 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.rerunprchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.rerunprchecks-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-checks-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.resolvereviewthread-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.resolvereviewthread-1.json index 58f02e037bf..0c1d9b8befd 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.resolvereviewthread-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.resolvereviewthread-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-checks-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.setprfileviewed-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.setprfileviewed-1.json index 2c82f18a59f..a9e6a86c142 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.setprfileviewed-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-checks-files-github.setprfileviewed-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-checks-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-github-github.addissuecomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-github-github.addissuecomment-1.json index 9421433810b..bb62ea8ebd8 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-github-github.addissuecomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-github-github.addissuecomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-comment-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-gitlab.addissuecomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-gitlab.addissuecomment-1.json index 6d4ea7fe995..78c7295bf6a 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-gitlab.addissuecomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-gitlab.addissuecomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-comment-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-mr-gitlab.addmrcomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-mr-gitlab.addmrcomment-1.json index ed3c57b2d36..667a14ba996 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-mr-gitlab.addmrcomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-comment-gitlab-mr-gitlab.addmrcomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-comment-gitlab-mr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-github-github.workitemdetails-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-github-github.workitemdetails-1.json index a2a086c6f22..39eefa4b43a 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-github-github.workitemdetails-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-github-github.workitemdetails-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-gitlab-gitlab.workitemdetails-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-gitlab-gitlab.workitemdetails-1.json index 9855cba3bd0..93e8bbb406d 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-gitlab-gitlab.workitemdetails-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-gitlab-gitlab.workitemdetails-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.getissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.getissue-1.json index 702ff101be4..869b00d61cd 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.getissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.getissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.issuecomments-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.issuecomments-1.json index 033943ccd1f..faeea3c5558 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.issuecomments-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-linear-linear.issuecomments-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listassignableusers-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listassignableusers-1.json index 3d06fb953fc..95f7f49b8e7 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listassignableusers-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listassignableusers-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "58ea1553e04017c993aea4753aace41ee664705a3fdb3b18569c5a9d7968cf06", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listlabels-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listlabels-1.json index 3a68b9f3621..ac686c98310 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listlabels-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-detail-metadata-github.listlabels-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "58ea1553e04017c993aea4753aace41ee664705a3fdb3b18569c5a9d7968cf06", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-merge-gitlab-gitlab.mergemr-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-merge-gitlab-gitlab.mergemr-1.json index cb12175440a..eed90edd81b 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-merge-gitlab-gitlab.mergemr-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-merge-gitlab-gitlab.mergemr-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-merge-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-github-github.updatepr-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-github-github.updatepr-1.json index 68c00a9be32..48bb2ba53d8 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-github-github.updatepr-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-github-github.updatepr-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-metadata-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-gitlab.updateissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-gitlab.updateissue-1.json index 3889eacdb3b..45227c0f1e7 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-gitlab.updateissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-gitlab.updateissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-metadata-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-mr-gitlab.updatemr-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-mr-gitlab.updatemr-1.json index bfb4f34d7f5..c84a464eacb 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-mr-gitlab.updatemr-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-metadata-gitlab-mr-gitlab.updatemr-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-metadata-gitlab-mr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addissuecomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addissuecomment-1.json index fb6a16f6fd5..c2ceae9b35e 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addissuecomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addissuecomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-reply-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addprreviewcommentreply-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addprreviewcommentreply-1.json index a7075cb7037..3d41447db5c 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addprreviewcommentreply-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.addprreviewcommentreply-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-reply-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.mergepr-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.mergepr-1.json index da413ea302d..9c151d4f4a3 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.mergepr-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-github.mergepr-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-reply-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-linear.updateissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-linear.updateissue-1.json index 648da246f0e..bfad96ab441 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-linear.updateissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-reply-merge-linear.updateissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-reply-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.prchecks-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.prchecks-1.json index e6a66829415..6c6b01217e5 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.prchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.prchecks-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-review-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.requestprreviewers-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.requestprreviewers-1.json index f58b3f246fb..fe47471b166 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.requestprreviewers-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-review-github-github.requestprreviewers-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-review-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-github.updateissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-github.updateissue-1.json index 01fa11bb598..833e83874e4 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-github.updateissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-github.updateissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-status-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-gitlab.updateissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-gitlab.updateissue-1.json index 10c0ece2abb..c7daed8a6ff 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-gitlab.updateissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-gitlab.updateissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-status-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-mr-gitlab.updatemrstate-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-mr-gitlab.updatemrstate-1.json index cd0751c12b7..b65e1ca6f32 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-mr-gitlab.updatemrstate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.item-status-gitlab-mr-gitlab.updatemrstate-1.json @@ -3,7 +3,7 @@ "family": "tasks.item-status-gitlab-mr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.linear-connect-linear.connect-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.linear-connect-linear.connect-1.json index 382043a579e..4d747f62258 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.linear-connect-linear.connect-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.linear-connect-linear.connect-1.json @@ -3,7 +3,7 @@ "family": "tasks.linear-connect", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.addissuecomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.addissuecomment-1.json index 7a0ca0436e7..5f061261f78 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.addissuecomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.addissuecomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.linear-item", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.createissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.createissue-1.json index 6eea8946082..ba8e76a170c 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.createissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.createissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.linear-item", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.getissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.getissue-1.json index e9070dfae3f..bfb0720ca99 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.getissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.linear-item-linear.getissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.linear-item", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.listteams-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.listteams-1.json index f861bef99b1..0e530995693 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.listteams-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.listteams-1.json @@ -3,7 +3,7 @@ "family": "tasks.linear-team-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "58ea1553e04017c993aea4753aace41ee664705a3fdb3b18569c5a9d7968cf06", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.teamstates-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.teamstates-1.json index 505b6397aca..5df25ca2886 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.teamstates-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.linear-team-context-linear.teamstates-1.json @@ -3,7 +3,7 @@ "family": "tasks.linear-team-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "58ea1553e04017c993aea4753aace41ee664705a3fdb3b18569c5a9d7968cf06", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.json index 741d7300ad5..e8d6b37a7c8 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.json index e2b0a492671..4dcc3ea9d23 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.json index 306c8da4799..87b03e93480 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.json index 06510a4ba13..893e8f549a1 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listaccessible-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listaccessible-1.json index b0083056f35..adf542f5234 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listaccessible-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listaccessible-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-board-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-1.json index 5f6a9115b33..ab8b0eed755 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-board-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-2.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-2.json index 1cb2a7f8e1f..14605033277 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-2.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.listviews-2.json @@ -3,7 +3,7 @@ "family": "tasks.project-board-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.resolveref-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.resolveref-1.json index 724480e2513..fde9517ee04 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.resolveref-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.resolveref-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-board-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.viewtable-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.viewtable-1.json index 6c4de159aa3..b674292d190 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.viewtable-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-board-load-github.project.viewtable-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-board-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-repo-slugs-github.reposlug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-repo-slugs-github.reposlug-1.json index 989df660c39..453a1561602 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-repo-slugs-github.reposlug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-repo-slugs-github.reposlug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-repo-slugs", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.addissuecommentbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.addissuecommentbyslug-1.json index e1ac164e4c6..20b67c2ab40 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.addissuecommentbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.addissuecommentbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-comments-issue", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuebyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuebyslug-1.json index aaedb2b6a00..317f36d79c6 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuebyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuebyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-comments-issue", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuecommentbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuecommentbyslug-1.json index de82cc03bac..56c79020dce 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuecommentbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-issue-github.project.updateissuecommentbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-comments-issue", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-pr-github.project.updatepullrequestbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-pr-github.project.updatepullrequestbyslug-1.json index d538a1885c4..67d69a7ab6a 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-pr-github.project.updatepullrequestbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-comments-pr-github.project.updatepullrequestbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-comments-pr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-detail-github.project.workitemdetailsbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-detail-github.project.workitemdetailsbyslug-1.json index 80ac3991978..1cfe4867f76 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-detail-github.project.workitemdetailsbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-detail-github.project.workitemdetailsbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-detail", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f8f6e5d500f959b9b15c5498885a05422747880b6aef4ad795bc3064ebbacea6", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.clearitemfield-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.clearitemfield-1.json index c4a7cc1ae22..ed7a10ce717 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.clearitemfield-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.clearitemfield-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-fields", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateissuetypebyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateissuetypebyslug-1.json index 3086b491114..a48f0052fc5 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateissuetypebyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateissuetypebyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-fields", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateitemfield-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateitemfield-1.json index f451f24d47e..00f3315e7d2 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateitemfield-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-fields-github.project.updateitemfield-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-fields", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.addprreviewcomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.addprreviewcomment-1.json index b47246133b6..36ed9fb613e 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.addprreviewcomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.addprreviewcomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-files-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b228732762828412ad3d9eec3ece00a897d866046e37044322c3911758d6e0a9", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.mergepr-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.mergepr-1.json index efd18b1fe60..7f993ff28a8 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.mergepr-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.mergepr-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-files-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b228732762828412ad3d9eec3ece00a897d866046e37044322c3911758d6e0a9", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.prfilecontents-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.prfilecontents-1.json index cbef12adfa2..24cf716a6b8 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.prfilecontents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.prfilecontents-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-files-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b228732762828412ad3d9eec3ece00a897d866046e37044322c3911758d6e0a9", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateissue-1.json index 96530b21a0c..ffb1aedd812 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-files-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b228732762828412ad3d9eec3ece00a897d866046e37044322c3911758d6e0a9", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateprstate-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateprstate-1.json index 37b12396039..ba9a71949c7 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateprstate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-files-merge-github.updateprstate-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-files-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b228732762828412ad3d9eec3ece00a897d866046e37044322c3911758d6e0a9", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listassignableusersbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listassignableusersbyslug-1.json index 59486a1564a..e2b20f7f3b4 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listassignableusersbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listassignableusersbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-metadata-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f8f6e5d500f959b9b15c5498885a05422747880b6aef4ad795bc3064ebbacea6", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listissuetypesbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listissuetypesbyslug-1.json index 03fdd96ab2a..38a4cd1f9f0 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listissuetypesbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listissuetypesbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-metadata-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f8f6e5d500f959b9b15c5498885a05422747880b6aef4ad795bc3064ebbacea6", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listlabelsbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listlabelsbyslug-1.json index 47d90225433..e23de516bec 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listlabelsbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-metadata-load-github.project.listlabelsbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-metadata-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f8f6e5d500f959b9b15c5498885a05422747880b6aef4ad795bc3064ebbacea6", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.prchecks-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.prchecks-1.json index 9d0490d2a99..8fdf368f9d1 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.prchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.prchecks-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-review-checks", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.requestprreviewers-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.requestprreviewers-1.json index 32568e1064e..3f80e0f8f5c 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.requestprreviewers-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.requestprreviewers-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-review-checks", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.rerunprchecks-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.rerunprchecks-1.json index cdedfced984..c04371a1c01 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.rerunprchecks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.rerunprchecks-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-review-checks", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.setprfileviewed-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.setprfileviewed-1.json index aeef728df1d..b515f4a7adf 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.setprfileviewed-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-review-checks-github.setprfileviewed-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-review-checks", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addissuecomment-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addissuecomment-1.json index 7eedede0ae5..d5b98105954 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addissuecomment-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addissuecomment-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-threads", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addprreviewcommentreply-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addprreviewcommentreply-1.json index 387a5c3ef2c..57623a1bdc1 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addprreviewcommentreply-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.addprreviewcommentreply-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-threads", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.project.deleteissuecommentbyslug-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.project.deleteissuecommentbyslug-1.json index 4554867bbab..eb9dd2ff0b6 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.project.deleteissuecommentbyslug-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.project.deleteissuecommentbyslug-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-threads", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.resolvereviewthread-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.resolvereviewthread-1.json index 9fba9c25c56..c19eafc7183 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.resolvereviewthread-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.project-row-threads-github.resolvereviewthread-1.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-threads", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.countworkitems-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.countworkitems-1.json index 8faf0a86208..defd19d552a 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.countworkitems-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.countworkitems-1.json @@ -3,7 +3,7 @@ "family": "tasks.provider-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.listworkitems-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.listworkitems-1.json index c5196794afc..bb58c7b4f4a 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.listworkitems-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-github.listworkitems-1.json @@ -3,7 +3,7 @@ "family": "tasks.provider-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.listteams-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.listteams-1.json index 659ba94f39a..f98dcbdd2a4 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.listteams-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.listteams-1.json @@ -3,7 +3,7 @@ "family": "tasks.provider-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.status-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.status-1.json index ffb36d12a77..f1892f399c6 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.status-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-linear.status-1.json @@ -3,7 +3,7 @@ "family": "tasks.provider-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-settings.update-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-settings.update-1.json index b4fb1f682c1..ddc44454e8a 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-settings.update-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.provider-load-settings.update-1.json @@ -3,7 +3,7 @@ "family": "tasks.provider-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.route-repo-list-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.route-repo-list-repo.list-1.json index e83bf010410..32be0427f6a 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.route-repo-list-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.route-repo-list-repo.list-1.json @@ -3,7 +3,7 @@ "family": "tasks.route-repo-list", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "feb6cee1ab7ecff1ba98bfba22d4924c748d3bb6b749db460cb617ee50b92f2c", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.json index f9b17812895..9f7c7b6b74d 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.json index 4d83e4407bf..2fbd566d63e 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json index e7a9b74fedb..61f9b2b1c1e 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.json index 6b191290cb7..6a699c04bdd 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.json index bee1f7226cf..c7d714c4294 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-github.createissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-github.createissue-1.json index f345a3fc18c..d04a37f6c51 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-github.createissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-github.createissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-repo.update-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-repo.update-1.json index 8c3de55e342..ce0a75442b8 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-repo.update-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-github-repo.update-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-gitlab-gitlab.createissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-gitlab-gitlab.createissue-1.json index 21fb2acd90d..55ccc7feefb 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-gitlab-gitlab.createissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-gitlab-gitlab.createissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-linear-linear.createissue-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-linear-linear.createissue-1.json index d228fb497fb..e69245d7f2b 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-create-linear-linear.createissue-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-create-linear-linear.createissue-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-items-gitlab.listworkitems-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-items-gitlab.listworkitems-1.json index 5e8c187a1d2..030012450ba 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-items-gitlab.listworkitems-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-items-gitlab.listworkitems-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-gitlab-items", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-todos-gitlab.todos-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-todos-gitlab.todos-1.json index 34b80e99406..7be4c82de44 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-todos-gitlab.todos-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-gitlab-todos-gitlab.todos-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-gitlab-todos", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.listissues-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.listissues-1.json index 604bdb34d8b..9c3446138c2 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.listissues-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.listissues-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.searchissues-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.searchissues-1.json index b8f57d7287e..cd42a40e6bb 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.searchissues-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.task-list-linear-linear.searchissues-1.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json index ed50193690d..dbe8f008b87 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-source", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.json index 6066e0dc301..eda9b8d68c1 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-source", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json index c7864af77c7..14873e66b8d 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-sparse", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json index 00ff849e309..90982b7a0b4 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-sparse", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.json index 8bc23761fd1..62484b9d432 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh-local", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.json index 918d659d2d4..3e268610e7c 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.json index ababdf06fd2..651ff3e50f6 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json index 21d92d28e5d..d0eaf4a2cd1 100644 --- a/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json +++ b/mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/matrix-terminal.query-reply-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-terminal.query-reply-terminal.send-1.json index 50eab746bb5..19eafa4df51 100644 --- a/mobile/rpc-foundation/goldens/matrix-terminal.query-reply-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-terminal.query-reply-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "terminal.query-reply", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-orchestration.workerterminaluserinput-1.json index 756931e6580..9f098021d62 100644 --- a/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "terminal.raw-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-terminal.send-1.json b/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-terminal.send-1.json index e06766b3d87..b3f99250f74 100644 --- a/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-terminal.send-1.json +++ b/mobile/rpc-foundation/goldens/matrix-terminal.raw-input-terminal.send-1.json @@ -3,7 +3,7 @@ "family": "terminal.raw-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-1.json b/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-1.json index 06753b20b22..4e231605dec 100644 --- a/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-1.json +++ b/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-1.json @@ -3,7 +3,7 @@ "family": "terminal.takeover-report", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-2.json b/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-2.json index e1a427cac8e..70e703015c5 100644 --- a/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-2.json +++ b/mobile/rpc-foundation/goldens/matrix-terminal.takeover-report-orchestration.workerterminaluserinput-2.json @@ -3,7 +3,7 @@ "family": "terminal.takeover-report", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/matrix-terminal.viewport-refit-terminal.updateviewport-1.json b/mobile/rpc-foundation/goldens/matrix-terminal.viewport-refit-terminal.updateviewport-1.json index 7a6918ad255..fc2b147383a 100644 --- a/mobile/rpc-foundation/goldens/matrix-terminal.viewport-refit-terminal.updateviewport-1.json +++ b/mobile/rpc-foundation/goldens/matrix-terminal.viewport-refit-terminal.updateviewport-1.json @@ -3,7 +3,7 @@ "family": "terminal.viewport-refit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.json index 3c9a933656e..38bf5dc65c0 100644 --- a/mobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.json @@ -3,7 +3,7 @@ "family": "transport.capability-probe", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.json index 3a2356fc7d3..432d19598b4 100644 --- a/mobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.json @@ -3,7 +3,7 @@ "family": "transport.host-status-gates", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.json b/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.json index c19b1042087..b2fb322c5ab 100644 --- a/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.json +++ b/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.json @@ -3,7 +3,7 @@ "family": "transport.pairing-race", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.json b/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.json index 488d081c6ac..153490a530b 100644 --- a/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.json +++ b/mobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.json @@ -3,7 +3,7 @@ "family": "transport.pairing-race", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.agent-launch-create-agent.launch-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.agent-launch-create-agent.launch-1.json index b62925fdcb5..8c11f56349f 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.agent-launch-create-agent.launch-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.agent-launch-create-agent.launch-1.json @@ -3,7 +3,7 @@ "family": "worktree.agent-launch-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.catalog-snapshot-worktree.ps-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.catalog-snapshot-worktree.ps-1.json index fdeab28aa9d..cc0cde5daf3 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.catalog-snapshot-worktree.ps-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.catalog-snapshot-worktree.ps-1.json @@ -3,7 +3,7 @@ "family": "worktree.catalog-snapshot", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json index acaeb2b32e9..41c9542e10b 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.home-catalog-worktree.ps-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.home-catalog-worktree.ps-1.json index dd6ce21a045..d5c0be05c90 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.home-catalog-worktree.ps-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.home-catalog-worktree.ps-1.json @@ -3,7 +3,7 @@ "family": "worktree.home-catalog", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.json index abcbee1d756..a4a4449462f 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.json @@ -3,7 +3,7 @@ "family": "worktree.hosted-base", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.json index 57aafc46bb1..371ef955a32 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.json @@ -3,7 +3,7 @@ "family": "worktree.hosted-base", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.retired-names-worktree.listretirednames-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.retired-names-worktree.listretirednames-1.json index 9861f3f313a..c79bbe5dd6d 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.retired-names-worktree.listretirednames-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.retired-names-worktree.listretirednames-1.json @@ -3,7 +3,7 @@ "family": "worktree.retired-names", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.json index ab4dfda570a..f0bbd3b3330 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.json @@ -3,7 +3,7 @@ "family": "worktree.review-link", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.json index c270946be74..aea34ba1ebe 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.json @@ -3,7 +3,7 @@ "family": "worktree.runtime-capabilities", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.json b/mobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.json index 011e86777de..09e7a292685 100644 --- a/mobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.json +++ b/mobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.json @@ -3,7 +3,7 @@ "family": "worktree.setup-hook-trust", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-paste-single.json b/mobile/rpc-foundation/goldens/native-chat-image-paste-single.json index 2b887a29805..444671a68fd 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-paste-single.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-paste-single.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-paste-stops-on-rejection.json b/mobile/rpc-foundation/goldens/native-chat-image-paste-stops-on-rejection.json index a1cf7db96e5..cbffd086a94 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-paste-stops-on-rejection.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-paste-stops-on-rejection.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-paste-trailing-image.json b/mobile/rpc-foundation/goldens/native-chat-image-paste-trailing-image.json index fea9ab22020..797b76573cd 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-paste-trailing-image.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-paste-trailing-image.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-paste-two-images.json b/mobile/rpc-foundation/goldens/native-chat-image-paste-two-images.json index 791e6a62923..42e968007b1 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-paste-two-images.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-paste-two-images.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-upload-cancelled.json b/mobile/rpc-foundation/goldens/native-chat-image-upload-cancelled.json index 3fc3c75d837..efec3633436 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-upload-cancelled.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-upload-cancelled.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-upload-second-fails.json b/mobile/rpc-foundation/goldens/native-chat-image-upload-second-fails.json index 4ea8a3db5d4..ef7179a8681 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-upload-second-fails.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-upload-second-fails.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-upload-single.json b/mobile/rpc-foundation/goldens/native-chat-image-upload-single.json index edcded11ee3..e54fa99f4f2 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-upload-single.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-upload-single.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-upload-start-refused.json b/mobile/rpc-foundation/goldens/native-chat-image-upload-start-refused.json index 9b9eaf4e689..51e2383d91c 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-upload-start-refused.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-upload-start-refused.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-image-upload-two.json b/mobile/rpc-foundation/goldens/native-chat-image-upload-two.json index 64e6ca77092..92d33cf4403 100644 --- a/mobile/rpc-foundation/goldens/native-chat-image-upload-two.json +++ b/mobile/rpc-foundation/goldens/native-chat-image-upload-two.json @@ -3,7 +3,7 @@ "family": "nativeChat.image-upload", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "16779b663f4faec8cbccdb42efd7b568a2912845b161f8a4827754687eb4235c", diff --git a/mobile/rpc-foundation/goldens/native-chat-page-earlier.json b/mobile/rpc-foundation/goldens/native-chat-page-earlier.json index 66bf225b507..4cd32bd4dc4 100644 --- a/mobile/rpc-foundation/goldens/native-chat-page-earlier.json +++ b/mobile/rpc-foundation/goldens/native-chat-page-earlier.json @@ -3,7 +3,7 @@ "family": "session.native-chat-page", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "60ce67f66134d6385aa7fb47f2135436a7b3ce0221e3b2514bea65e06dba5518", diff --git a/mobile/rpc-foundation/goldens/native-chat-readability-local-repo.json b/mobile/rpc-foundation/goldens/native-chat-readability-local-repo.json index 50495ff505e..9c5dd5bba2a 100644 --- a/mobile/rpc-foundation/goldens/native-chat-readability-local-repo.json +++ b/mobile/rpc-foundation/goldens/native-chat-readability-local-repo.json @@ -3,7 +3,7 @@ "family": "session.native-chat-readability", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/native-chat-readability-refused.json b/mobile/rpc-foundation/goldens/native-chat-readability-refused.json index 85f12f68a49..cd48baf6367 100644 --- a/mobile/rpc-foundation/goldens/native-chat-readability-refused.json +++ b/mobile/rpc-foundation/goldens/native-chat-readability-refused.json @@ -3,7 +3,7 @@ "family": "session.native-chat-readability", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/native-chat-readability-remote-repo.json b/mobile/rpc-foundation/goldens/native-chat-readability-remote-repo.json index dbc8023cb1b..208face6ecf 100644 --- a/mobile/rpc-foundation/goldens/native-chat-readability-remote-repo.json +++ b/mobile/rpc-foundation/goldens/native-chat-readability-remote-repo.json @@ -3,7 +3,7 @@ "family": "session.native-chat-readability", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/native-chat-session-option-pick-empty.json b/mobile/rpc-foundation/goldens/native-chat-session-option-pick-empty.json index e0c555ecbca..31eb6efeea2 100644 --- a/mobile/rpc-foundation/goldens/native-chat-session-option-pick-empty.json +++ b/mobile/rpc-foundation/goldens/native-chat-session-option-pick-empty.json @@ -3,7 +3,7 @@ "family": "nativeChat.session-option-pick", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-session-option-pick-refused.json b/mobile/rpc-foundation/goldens/native-chat-session-option-pick-refused.json index 6499355e828..7919382bf7c 100644 --- a/mobile/rpc-foundation/goldens/native-chat-session-option-pick-refused.json +++ b/mobile/rpc-foundation/goldens/native-chat-session-option-pick-refused.json @@ -3,7 +3,7 @@ "family": "nativeChat.session-option-pick", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-session-option-pick-written.json b/mobile/rpc-foundation/goldens/native-chat-session-option-pick-written.json index 6d0018d3fe2..1590b889ea6 100644 --- a/mobile/rpc-foundation/goldens/native-chat-session-option-pick-written.json +++ b/mobile/rpc-foundation/goldens/native-chat-session-option-pick-written.json @@ -3,7 +3,7 @@ "family": "nativeChat.session-option-pick", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-stop-accepted.json b/mobile/rpc-foundation/goldens/native-chat-stop-accepted.json index f89e1a1e346..3e794edddcc 100644 --- a/mobile/rpc-foundation/goldens/native-chat-stop-accepted.json +++ b/mobile/rpc-foundation/goldens/native-chat-stop-accepted.json @@ -3,7 +3,7 @@ "family": "session.native-chat-stop", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/native-chat-stop-both-rejected.json b/mobile/rpc-foundation/goldens/native-chat-stop-both-rejected.json index 2dd1036dcd0..d178b4f7aff 100644 --- a/mobile/rpc-foundation/goldens/native-chat-stop-both-rejected.json +++ b/mobile/rpc-foundation/goldens/native-chat-stop-both-rejected.json @@ -3,7 +3,7 @@ "family": "session.native-chat-stop", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/native-chat-stop-delivery-unknown.json b/mobile/rpc-foundation/goldens/native-chat-stop-delivery-unknown.json index 774fd467bbb..eea930a68ad 100644 --- a/mobile/rpc-foundation/goldens/native-chat-stop-delivery-unknown.json +++ b/mobile/rpc-foundation/goldens/native-chat-stop-delivery-unknown.json @@ -3,7 +3,7 @@ "family": "session.native-chat-stop", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/native-chat-write-accepted.json b/mobile/rpc-foundation/goldens/native-chat-write-accepted.json index 99affcf63dd..965eec2bcd7 100644 --- a/mobile/rpc-foundation/goldens/native-chat-write-accepted.json +++ b/mobile/rpc-foundation/goldens/native-chat-write-accepted.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-write-clear-line.json b/mobile/rpc-foundation/goldens/native-chat-write-clear-line.json index 6f0ed593047..82f6be70c62 100644 --- a/mobile/rpc-foundation/goldens/native-chat-write-clear-line.json +++ b/mobile/rpc-foundation/goldens/native-chat-write-clear-line.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-write-delivery-unknown.json b/mobile/rpc-foundation/goldens/native-chat-write-delivery-unknown.json index b7294b083ca..9c509bd7690 100644 --- a/mobile/rpc-foundation/goldens/native-chat-write-delivery-unknown.json +++ b/mobile/rpc-foundation/goldens/native-chat-write-delivery-unknown.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-write-rejected.json b/mobile/rpc-foundation/goldens/native-chat-write-rejected.json index 9abaeb2b2a8..900bd952534 100644 --- a/mobile/rpc-foundation/goldens/native-chat-write-rejected.json +++ b/mobile/rpc-foundation/goldens/native-chat-write-rejected.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/native-chat-write-typed-command.json b/mobile/rpc-foundation/goldens/native-chat-write-typed-command.json index 8ef29a4af7c..86f6ca5debe 100644 --- a/mobile/rpc-foundation/goldens/native-chat-write-typed-command.json +++ b/mobile/rpc-foundation/goldens/native-chat-write-typed-command.json @@ -3,7 +3,7 @@ "family": "nativeChat.terminal-write", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b98a3ca3818678b88cf629bc4635300a103637126a136f025a1600dc16f08008", diff --git a/mobile/rpc-foundation/goldens/new-tab-local-agents.json b/mobile/rpc-foundation/goldens/new-tab-local-agents.json index 816c44d143a..1ef040d0833 100644 --- a/mobile/rpc-foundation/goldens/new-tab-local-agents.json +++ b/mobile/rpc-foundation/goldens/new-tab-local-agents.json @@ -3,7 +3,7 @@ "family": "settings.new-tab-local-agents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/new-workspace-repositories-fulfilled.json b/mobile/rpc-foundation/goldens/new-workspace-repositories-fulfilled.json index 216623e6280..aabf42cb85e 100644 --- a/mobile/rpc-foundation/goldens/new-workspace-repositories-fulfilled.json +++ b/mobile/rpc-foundation/goldens/new-workspace-repositories-fulfilled.json @@ -3,7 +3,7 @@ "family": "components.new-workspace-repositories", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "64c1772f0f95a3c43fbb14398a8804b2b4784f7f18874e4fd79767ae634c7faa", diff --git a/mobile/rpc-foundation/goldens/notifications-desktop-stream-closed.json b/mobile/rpc-foundation/goldens/notifications-desktop-stream-closed.json index d27391e7558..9dcc9cdd0ee 100644 --- a/mobile/rpc-foundation/goldens/notifications-desktop-stream-closed.json +++ b/mobile/rpc-foundation/goldens/notifications-desktop-stream-closed.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/notifications-desktop-stream-replayed.json b/mobile/rpc-foundation/goldens/notifications-desktop-stream-replayed.json index 2596b9aa6a2..1777a97f59e 100644 --- a/mobile/rpc-foundation/goldens/notifications-desktop-stream-replayed.json +++ b/mobile/rpc-foundation/goldens/notifications-desktop-stream-replayed.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/notifications-desktop-stream.json b/mobile/rpc-foundation/goldens/notifications-desktop-stream.json index 4404ce96fce..0371276e529 100644 --- a/mobile/rpc-foundation/goldens/notifications-desktop-stream.json +++ b/mobile/rpc-foundation/goldens/notifications-desktop-stream.json @@ -3,7 +3,7 @@ "family": "notifications.desktop-stream", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "eacf859143588ae6bee2804975d642b6c1088d57ae77ffe298620250d9a9f0e4", diff --git a/mobile/rpc-foundation/goldens/notifications-display-test-accepted.json b/mobile/rpc-foundation/goldens/notifications-display-test-accepted.json index f9b3572b0cf..38d6dee8ea5 100644 --- a/mobile/rpc-foundation/goldens/notifications-display-test-accepted.json +++ b/mobile/rpc-foundation/goldens/notifications-display-test-accepted.json @@ -3,7 +3,7 @@ "family": "notifications.display-test-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "12a04986290715d5db23a1eb5192c1138bb2d375d1b1dd37ba310ea89eb11566", diff --git a/mobile/rpc-foundation/goldens/notifications-display-test-not-registered.json b/mobile/rpc-foundation/goldens/notifications-display-test-not-registered.json index dcf9ed6c388..347e627608d 100644 --- a/mobile/rpc-foundation/goldens/notifications-display-test-not-registered.json +++ b/mobile/rpc-foundation/goldens/notifications-display-test-not-registered.json @@ -3,7 +3,7 @@ "family": "notifications.display-test-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "12a04986290715d5db23a1eb5192c1138bb2d375d1b1dd37ba310ea89eb11566", diff --git a/mobile/rpc-foundation/goldens/notifications-display-test-rate-limited.json b/mobile/rpc-foundation/goldens/notifications-display-test-rate-limited.json index 40058fe7310..f22bf7203bc 100644 --- a/mobile/rpc-foundation/goldens/notifications-display-test-rate-limited.json +++ b/mobile/rpc-foundation/goldens/notifications-display-test-rate-limited.json @@ -3,7 +3,7 @@ "family": "notifications.display-test-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "12a04986290715d5db23a1eb5192c1138bb2d375d1b1dd37ba310ea89eb11566", diff --git a/mobile/rpc-foundation/goldens/notifications-display-test-unknown-reason.json b/mobile/rpc-foundation/goldens/notifications-display-test-unknown-reason.json index 8f4120ebf75..b29c6619ea3 100644 --- a/mobile/rpc-foundation/goldens/notifications-display-test-unknown-reason.json +++ b/mobile/rpc-foundation/goldens/notifications-display-test-unknown-reason.json @@ -3,7 +3,7 @@ "family": "notifications.display-test-screen", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "12a04986290715d5db23a1eb5192c1138bb2d375d1b1dd37ba310ea89eb11566", diff --git a/mobile/rpc-foundation/goldens/notifications-push-gateway-rejected.json b/mobile/rpc-foundation/goldens/notifications-push-gateway-rejected.json index 42e95ac2281..b054aab49c3 100644 --- a/mobile/rpc-foundation/goldens/notifications-push-gateway-rejected.json +++ b/mobile/rpc-foundation/goldens/notifications-push-gateway-rejected.json @@ -3,7 +3,7 @@ "family": "notifications.push-registration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2e3d939dc162dbc5a38d8a7207111688204a825fd70348721917b3016e1c9470", diff --git a/mobile/rpc-foundation/goldens/notifications-push-registered.json b/mobile/rpc-foundation/goldens/notifications-push-registered.json index 020d3df8cbb..d5f4fb848b0 100644 --- a/mobile/rpc-foundation/goldens/notifications-push-registered.json +++ b/mobile/rpc-foundation/goldens/notifications-push-registered.json @@ -3,7 +3,7 @@ "family": "notifications.push-registration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2e3d939dc162dbc5a38d8a7207111688204a825fd70348721917b3016e1c9470", diff --git a/mobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.json b/mobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.json index 4f4e22c10c1..767d60cd85c 100644 --- a/mobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.json +++ b/mobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.json b/mobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.json index 3b85ea1e8fe..1118719f3cf 100644 --- a/mobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.json +++ b/mobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/pairing-pre-profile-times-out.json b/mobile/rpc-foundation/goldens/pairing-pre-profile-times-out.json index 3864c539c18..c44d1ccfa25 100644 --- a/mobile/rpc-foundation/goldens/pairing-pre-profile-times-out.json +++ b/mobile/rpc-foundation/goldens/pairing-pre-profile-times-out.json @@ -3,7 +3,7 @@ "family": "pairing.pre-profile", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/pr-branch-identity.json b/mobile/rpc-foundation/goldens/pr-branch-identity.json index b0b734b181f..dcf51b1f0ef 100644 --- a/mobile/rpc-foundation/goldens/pr-branch-identity.json +++ b/mobile/rpc-foundation/goldens/pr-branch-identity.json @@ -3,7 +3,7 @@ "family": "session.pr-branch-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-branch-repo-context.json b/mobile/rpc-foundation/goldens/pr-branch-repo-context.json index 43898e1e240..64939c7fb24 100644 --- a/mobile/rpc-foundation/goldens/pr-branch-repo-context.json +++ b/mobile/rpc-foundation/goldens/pr-branch-repo-context.json @@ -3,7 +3,7 @@ "family": "session.pr-branch-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-comment-mutation.json b/mobile/rpc-foundation/goldens/pr-comment-mutation.json index 8f56f34d033..c4f801666f1 100644 --- a/mobile/rpc-foundation/goldens/pr-comment-mutation.json +++ b/mobile/rpc-foundation/goldens/pr-comment-mutation.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-comment-resolve-unconfirmed.json b/mobile/rpc-foundation/goldens/pr-comment-resolve-unconfirmed.json index 4a7b2a03575..03bc8591739 100644 --- a/mobile/rpc-foundation/goldens/pr-comment-resolve-unconfirmed.json +++ b/mobile/rpc-foundation/goldens/pr-comment-resolve-unconfirmed.json @@ -3,7 +3,7 @@ "family": "github.pr-comment-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-mutation-in-band-failure.json b/mobile/rpc-foundation/goldens/pr-mutation-in-band-failure.json index 6a2a6a0b6b3..81998fc8939 100644 --- a/mobile/rpc-foundation/goldens/pr-mutation-in-band-failure.json +++ b/mobile/rpc-foundation/goldens/pr-mutation-in-band-failure.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-mutation-status.json b/mobile/rpc-foundation/goldens/pr-mutation-status.json index 8f4870b8652..6f02667da82 100644 --- a/mobile/rpc-foundation/goldens/pr-mutation-status.json +++ b/mobile/rpc-foundation/goldens/pr-mutation-status.json @@ -3,7 +3,7 @@ "family": "github.pr-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-read-fork-routing.json b/mobile/rpc-foundation/goldens/pr-read-fork-routing.json index 8a51dfd96a8..dcb7df25732 100644 --- a/mobile/rpc-foundation/goldens/pr-read-fork-routing.json +++ b/mobile/rpc-foundation/goldens/pr-read-fork-routing.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-read-surface.json b/mobile/rpc-foundation/goldens/pr-read-surface.json index fd2bf46bf60..cc681664739 100644 --- a/mobile/rpc-foundation/goldens/pr-read-surface.json +++ b/mobile/rpc-foundation/goldens/pr-read-surface.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-read-upstream-error.json b/mobile/rpc-foundation/goldens/pr-read-upstream-error.json index 2fce3409f87..4ff45e2295b 100644 --- a/mobile/rpc-foundation/goldens/pr-read-upstream-error.json +++ b/mobile/rpc-foundation/goldens/pr-read-upstream-error.json @@ -3,7 +3,7 @@ "family": "github.pr-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-sidebar-checks-refused.json b/mobile/rpc-foundation/goldens/pr-sidebar-checks-refused.json index 9d2a21e3bf6..2825650bd39 100644 --- a/mobile/rpc-foundation/goldens/pr-sidebar-checks-refused.json +++ b/mobile/rpc-foundation/goldens/pr-sidebar-checks-refused.json @@ -3,7 +3,7 @@ "family": "session.pr-sidebar", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "87ffa2daea415d2682f1112025b5bd9d52404c6fc7c239180a3ad2120678ff1f", diff --git a/mobile/rpc-foundation/goldens/pr-sidebar-load.json b/mobile/rpc-foundation/goldens/pr-sidebar-load.json index e96149d9f02..c0bbccf2ab5 100644 --- a/mobile/rpc-foundation/goldens/pr-sidebar-load.json +++ b/mobile/rpc-foundation/goldens/pr-sidebar-load.json @@ -3,7 +3,7 @@ "family": "session.pr-sidebar", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "87ffa2daea415d2682f1112025b5bd9d52404c6fc7c239180a3ad2120678ff1f", diff --git a/mobile/rpc-foundation/goldens/pr-title-mutation.json b/mobile/rpc-foundation/goldens/pr-title-mutation.json index 1b9270611e7..c1c2f84ba33 100644 --- a/mobile/rpc-foundation/goldens/pr-title-mutation.json +++ b/mobile/rpc-foundation/goldens/pr-title-mutation.json @@ -3,7 +3,7 @@ "family": "github.pr-title-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-title-unconfirmed.json b/mobile/rpc-foundation/goldens/pr-title-unconfirmed.json index 2bd409127fd..54038386532 100644 --- a/mobile/rpc-foundation/goldens/pr-title-unconfirmed.json +++ b/mobile/rpc-foundation/goldens/pr-title-unconfirmed.json @@ -3,7 +3,7 @@ "family": "github.pr-title-mutation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-triage-invalid-terminal.json b/mobile/rpc-foundation/goldens/pr-triage-invalid-terminal.json index 7ae4ce93a97..ad09b4ece8b 100644 --- a/mobile/rpc-foundation/goldens/pr-triage-invalid-terminal.json +++ b/mobile/rpc-foundation/goldens/pr-triage-invalid-terminal.json @@ -3,7 +3,7 @@ "family": "session.pr-triage", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-triage-launch.json b/mobile/rpc-foundation/goldens/pr-triage-launch.json index 6a878439dcf..d353a66dd8b 100644 --- a/mobile/rpc-foundation/goldens/pr-triage-launch.json +++ b/mobile/rpc-foundation/goldens/pr-triage-launch.json @@ -3,7 +3,7 @@ "family": "session.pr-triage", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/pr-triage-send-locked.json b/mobile/rpc-foundation/goldens/pr-triage-send-locked.json index 76c10459225..036f17e8fcf 100644 --- a/mobile/rpc-foundation/goldens/pr-triage-send-locked.json +++ b/mobile/rpc-foundation/goldens/pr-triage-send-locked.json @@ -3,7 +3,7 @@ "family": "session.pr-triage", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f560ff1582487c118756c527faa1fb1f65e37ce22ca9573491fd4b2dbcedf38d", diff --git a/mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json b/mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json index d8fc583d638..2a7aeaaacdc 100644 --- a/mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json +++ b/mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json b/mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json index 40864e8f670..66cfc6eb89f 100644 --- a/mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json +++ b/mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.json b/mobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.json index 2b0c07b066c..65a6d3ed0b8 100644 --- a/mobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.json +++ b/mobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json b/mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json index ac211c53766..6b9bf370e48 100644 --- a/mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json +++ b/mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/push-dismissal-tray-reconciled.json b/mobile/rpc-foundation/goldens/push-dismissal-tray-reconciled.json index 87f4180c5f8..72190de61bd 100644 --- a/mobile/rpc-foundation/goldens/push-dismissal-tray-reconciled.json +++ b/mobile/rpc-foundation/goldens/push-dismissal-tray-reconciled.json @@ -3,7 +3,7 @@ "family": "notifications.push-dismissal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "595a3eb2994d0596b9fcd0707b175b4e978625053dfbc5c541b0350c0cbfb524", diff --git a/mobile/rpc-foundation/goldens/quick-commands-load-refused.json b/mobile/rpc-foundation/goldens/quick-commands-load-refused.json index b3be8010e88..f11395b82d0 100644 --- a/mobile/rpc-foundation/goldens/quick-commands-load-refused.json +++ b/mobile/rpc-foundation/goldens/quick-commands-load-refused.json @@ -3,7 +3,7 @@ "family": "settings.quick-commands", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/quick-commands-loaded-and-saved.json b/mobile/rpc-foundation/goldens/quick-commands-loaded-and-saved.json index fd4d8f61aeb..63b48f8b515 100644 --- a/mobile/rpc-foundation/goldens/quick-commands-loaded-and-saved.json +++ b/mobile/rpc-foundation/goldens/quick-commands-loaded-and-saved.json @@ -3,7 +3,7 @@ "family": "settings.quick-commands", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/quick-commands-save-refused-rolls-back.json b/mobile/rpc-foundation/goldens/quick-commands-save-refused-rolls-back.json index d7fce4d9474..0f8c1821178 100644 --- a/mobile/rpc-foundation/goldens/quick-commands-save-refused-rolls-back.json +++ b/mobile/rpc-foundation/goldens/quick-commands-save-refused-rolls-back.json @@ -3,7 +3,7 @@ "family": "settings.quick-commands", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/relay-direct-upgrade-commits.json b/mobile/rpc-foundation/goldens/relay-direct-upgrade-commits.json index 905172421fa..4f77f8cbb5e 100644 --- a/mobile/rpc-foundation/goldens/relay-direct-upgrade-commits.json +++ b/mobile/rpc-foundation/goldens/relay-direct-upgrade-commits.json @@ -3,7 +3,7 @@ "family": "relay.direct-upgrade", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.json b/mobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.json index 1d22fd580bf..2e73220748d 100644 --- a/mobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.json +++ b/mobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.json @@ -3,7 +3,7 @@ "family": "relay.direct-upgrade", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.json b/mobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.json index 3392e86f47c..2cc867acd1d 100644 --- a/mobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.json +++ b/mobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.json @@ -3,7 +3,7 @@ "family": "relay.pairing-recovery", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.json b/mobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.json index 9e81dfaebcc..83269b855a5 100644 --- a/mobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.json +++ b/mobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.json @@ -3,7 +3,7 @@ "family": "relay.pairing-recovery", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e33d584229530c716ecdc44d198b95fcfb4dfd9468fba7d5222ee3f122950197", diff --git a/mobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.json b/mobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.json index ca4c8a9a7fb..38ead4f641f 100644 --- a/mobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.json +++ b/mobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.json @@ -3,7 +3,7 @@ "family": "relay.credential-rotation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.json b/mobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.json index cdbae2d9d21..a18c839256a 100644 --- a/mobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.json +++ b/mobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.json @@ -3,7 +3,7 @@ "family": "relay.credential-rotation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "651e75383caf1b30c329dec2d5d4f0da5358c410402d03cbb087f39600d7a4d2", diff --git a/mobile/rpc-foundation/goldens/review-branch-diff-shapes.json b/mobile/rpc-foundation/goldens/review-branch-diff-shapes.json index 48a5b39e395..96118a231d8 100644 --- a/mobile/rpc-foundation/goldens/review-branch-diff-shapes.json +++ b/mobile/rpc-foundation/goldens/review-branch-diff-shapes.json @@ -3,7 +3,7 @@ "family": "session.review-branch-diff", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/review-create-terminal-refused.json b/mobile/rpc-foundation/goldens/review-create-terminal-refused.json index 9302dac0b6f..b15f5ca4b18 100644 --- a/mobile/rpc-foundation/goldens/review-create-terminal-refused.json +++ b/mobile/rpc-foundation/goldens/review-create-terminal-refused.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-file-diff-shapes.json b/mobile/rpc-foundation/goldens/review-file-diff-shapes.json index 11b479f7e3f..ccf041d5824 100644 --- a/mobile/rpc-foundation/goldens/review-file-diff-shapes.json +++ b/mobile/rpc-foundation/goldens/review-file-diff-shapes.json @@ -3,7 +3,7 @@ "family": "session.review-file-diff", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a8016eb61915cf80a3bdeb622ee67d35be8b4b9862a75e4ef2e8f4ff8e93e7f2", diff --git a/mobile/rpc-foundation/goldens/review-git-mutations-run.json b/mobile/rpc-foundation/goldens/review-git-mutations-run.json index 705b772d82b..8e307ffd1e3 100644 --- a/mobile/rpc-foundation/goldens/review-git-mutations-run.json +++ b/mobile/rpc-foundation/goldens/review-git-mutations-run.json @@ -3,7 +3,7 @@ "family": "session.review-git-mutations", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-mark-reviewed-persists.json b/mobile/rpc-foundation/goldens/review-mark-reviewed-persists.json index 9b19a79319f..fa3c53fec13 100644 --- a/mobile/rpc-foundation/goldens/review-mark-reviewed-persists.json +++ b/mobile/rpc-foundation/goldens/review-mark-reviewed-persists.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-mark-reviewed-rolls-back.json b/mobile/rpc-foundation/goldens/review-mark-reviewed-rolls-back.json index 4e7c7c34b81..461b89b9e62 100644 --- a/mobile/rpc-foundation/goldens/review-mark-reviewed-rolls-back.json +++ b/mobile/rpc-foundation/goldens/review-mark-reviewed-rolls-back.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-open-in-session.json b/mobile/rpc-foundation/goldens/review-open-in-session.json index b5233b57ca3..5a9cce71905 100644 --- a/mobile/rpc-foundation/goldens/review-open-in-session.json +++ b/mobile/rpc-foundation/goldens/review-open-in-session.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-send-notes-heals-stale-input.json b/mobile/rpc-foundation/goldens/review-send-notes-heals-stale-input.json index df87eea476c..385bb1f436d 100644 --- a/mobile/rpc-foundation/goldens/review-send-notes-heals-stale-input.json +++ b/mobile/rpc-foundation/goldens/review-send-notes-heals-stale-input.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-send-sheet-lists-terminals.json b/mobile/rpc-foundation/goldens/review-send-sheet-lists-terminals.json index 84a46fa40fb..1d16f9524be 100644 --- a/mobile/rpc-foundation/goldens/review-send-sheet-lists-terminals.json +++ b/mobile/rpc-foundation/goldens/review-send-sheet-lists-terminals.json @@ -3,7 +3,7 @@ "family": "session.review-send-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-stage-file.json b/mobile/rpc-foundation/goldens/review-stage-file.json index 9c7a8ce4d4b..cdcc87b0e74 100644 --- a/mobile/rpc-foundation/goldens/review-stage-file.json +++ b/mobile/rpc-foundation/goldens/review-stage-file.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/review-stage-refused.json b/mobile/rpc-foundation/goldens/review-stage-refused.json index d8c21fc875e..81e0b015ef1 100644 --- a/mobile/rpc-foundation/goldens/review-stage-refused.json +++ b/mobile/rpc-foundation/goldens/review-stage-refused.json @@ -3,7 +3,7 @@ "family": "session.diff-review-actions", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "2d72b8e68a66a906394167beb8c78f1c0521ca1e731976fd26963ec3bfa9cca4", diff --git a/mobile/rpc-foundation/goldens/sc-base-ref-default.json b/mobile/rpc-foundation/goldens/sc-base-ref-default.json index 779bb2d81dc..f4da33147c2 100644 --- a/mobile/rpc-foundation/goldens/sc-base-ref-default.json +++ b/mobile/rpc-foundation/goldens/sc-base-ref-default.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.json b/mobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.json index bb0b73dff83..573f545ccac 100644 --- a/mobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.json +++ b/mobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json b/mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json index c212b286e36..a2e6da4fbae 100644 --- a/mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json +++ b/mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json b/mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json index af4bc7ca24f..edebf722c35 100644 --- a/mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json +++ b/mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json @@ -3,7 +3,7 @@ "family": "git.base-ref-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-branch-diff-previewed.json b/mobile/rpc-foundation/goldens/sc-branch-diff-previewed.json index bbb6ca6f953..de497221ca0 100644 --- a/mobile/rpc-foundation/goldens/sc-branch-diff-previewed.json +++ b/mobile/rpc-foundation/goldens/sc-branch-diff-previewed.json @@ -3,7 +3,7 @@ "family": "git.branch-diff-preview", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/sc-changes-loaded.json b/mobile/rpc-foundation/goldens/sc-changes-loaded.json index 1d708ab4cbf..533dc7323d3 100644 --- a/mobile/rpc-foundation/goldens/sc-changes-loaded.json +++ b/mobile/rpc-foundation/goldens/sc-changes-loaded.json @@ -3,7 +3,7 @@ "family": "git.changes-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json b/mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json index 7ace5f71a09..577c683ba99 100644 --- a/mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json +++ b/mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json @@ -3,7 +3,7 @@ "family": "git.commit-message-ai", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-commit-message-canceled.json b/mobile/rpc-foundation/goldens/sc-commit-message-canceled.json index e488944d680..81b3651bfdd 100644 --- a/mobile/rpc-foundation/goldens/sc-commit-message-canceled.json +++ b/mobile/rpc-foundation/goldens/sc-commit-message-canceled.json @@ -3,7 +3,7 @@ "family": "git.commit-message-ai", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-commit-message-generated.json b/mobile/rpc-foundation/goldens/sc-commit-message-generated.json index 3a3fbba996e..56b69464f9e 100644 --- a/mobile/rpc-foundation/goldens/sc-commit-message-generated.json +++ b/mobile/rpc-foundation/goldens/sc-commit-message-generated.json @@ -3,7 +3,7 @@ "family": "git.commit-message-ai", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-create-existing-review.json b/mobile/rpc-foundation/goldens/sc-create-existing-review.json index b5737e433c9..3f27c0436ec 100644 --- a/mobile/rpc-foundation/goldens/sc-create-existing-review.json +++ b/mobile/rpc-foundation/goldens/sc-create-existing-review.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json b/mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json index 22267848ca5..e945cab4af0 100644 --- a/mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json +++ b/mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-create-intent-unlisted-provider.json b/mobile/rpc-foundation/goldens/sc-create-intent-unlisted-provider.json index 42a055e177a..704ac01a133 100644 --- a/mobile/rpc-foundation/goldens/sc-create-intent-unlisted-provider.json +++ b/mobile/rpc-foundation/goldens/sc-create-intent-unlisted-provider.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-intent", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.json b/mobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.json index 114d3fd3329..f7d0300a1f7 100644 --- a/mobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.json +++ b/mobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json b/mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json index 6c151f3eed8..f7d8e80e8be 100644 --- a/mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json +++ b/mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-create-refused-empty-message.json b/mobile/rpc-foundation/goldens/sc-create-refused-empty-message.json index 42f3c1798ae..b957d3a2c1d 100644 --- a/mobile/rpc-foundation/goldens/sc-create-refused-empty-message.json +++ b/mobile/rpc-foundation/goldens/sc-create-refused-empty-message.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-create-rejected-empty-message.json b/mobile/rpc-foundation/goldens/sc-create-rejected-empty-message.json index 280ce445156..6a6680c58d1 100644 --- a/mobile/rpc-foundation/goldens/sc-create-rejected-empty-message.json +++ b/mobile/rpc-foundation/goldens/sc-create-rejected-empty-message.json @@ -3,7 +3,7 @@ "family": "hostedReview.create-chain", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-eligibility-fetched.json b/mobile/rpc-foundation/goldens/sc-eligibility-fetched.json index c9b942c9475..fad0224e499 100644 --- a/mobile/rpc-foundation/goldens/sc-eligibility-fetched.json +++ b/mobile/rpc-foundation/goldens/sc-eligibility-fetched.json @@ -3,7 +3,7 @@ "family": "hostedReview.eligibility", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-history-commit-files.json b/mobile/rpc-foundation/goldens/sc-history-commit-files.json index fda5d616e63..3383685d6d9 100644 --- a/mobile/rpc-foundation/goldens/sc-history-commit-files.json +++ b/mobile/rpc-foundation/goldens/sc-history-commit-files.json @@ -3,7 +3,7 @@ "family": "git.history-commit-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "48ccada93f208a24160483e98ee94a771ab6d63222f29ac4bbd6979157f98333", diff --git a/mobile/rpc-foundation/goldens/sc-history-loaded.json b/mobile/rpc-foundation/goldens/sc-history-loaded.json index 3b9d93fd137..55a6c262a32 100644 --- a/mobile/rpc-foundation/goldens/sc-history-loaded.json +++ b/mobile/rpc-foundation/goldens/sc-history-loaded.json @@ -3,7 +3,7 @@ "family": "git.history-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-pr-link-hosted-review.json b/mobile/rpc-foundation/goldens/sc-pr-link-hosted-review.json index a7b7773a1fe..51102f75ca5 100644 --- a/mobile/rpc-foundation/goldens/sc-pr-link-hosted-review.json +++ b/mobile/rpc-foundation/goldens/sc-pr-link-hosted-review.json @@ -3,7 +3,7 @@ "family": "worktree.review-link", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-pr-link-read.json b/mobile/rpc-foundation/goldens/sc-pr-link-read.json index de57c8ad30f..22302197454 100644 --- a/mobile/rpc-foundation/goldens/sc-pr-link-read.json +++ b/mobile/rpc-foundation/goldens/sc-pr-link-read.json @@ -3,7 +3,7 @@ "family": "worktree.review-link", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-pr-link-set.json b/mobile/rpc-foundation/goldens/sc-pr-link-set.json index a77d23c8563..68f0f34ec38 100644 --- a/mobile/rpc-foundation/goldens/sc-pr-link-set.json +++ b/mobile/rpc-foundation/goldens/sc-pr-link-set.json @@ -3,7 +3,7 @@ "family": "worktree.review-link", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json b/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json index dd582353867..20ded6677a4 100644 --- a/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json +++ b/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json @@ -3,7 +3,7 @@ "family": "hostedReview.eligibility", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.json b/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.json index de46aedeb4f..2ab275eb433 100644 --- a/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.json +++ b/mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.json @@ -3,7 +3,7 @@ "family": "hostedReview.eligibility", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.json b/mobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.json index bacda6ec4e0..5954f75ebf2 100644 --- a/mobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.json +++ b/mobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.json @@ -3,7 +3,7 @@ "family": "git.remote-prerequisite", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-prerequisite-publish.json b/mobile/rpc-foundation/goldens/sc-prerequisite-publish.json index c67e3512986..fffe393ba23 100644 --- a/mobile/rpc-foundation/goldens/sc-prerequisite-publish.json +++ b/mobile/rpc-foundation/goldens/sc-prerequisite-publish.json @@ -3,7 +3,7 @@ "family": "git.remote-prerequisite", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-prerequisite-push.json b/mobile/rpc-foundation/goldens/sc-prerequisite-push.json index 264198b17ad..e014c9f38ad 100644 --- a/mobile/rpc-foundation/goldens/sc-prerequisite-push.json +++ b/mobile/rpc-foundation/goldens/sc-prerequisite-push.json @@ -3,7 +3,7 @@ "family": "git.remote-prerequisite", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json b/mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json index 1ec00f72d84..adf34d160e5 100644 --- a/mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json +++ b/mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json @@ -3,7 +3,7 @@ "family": "git.remote-prerequisite", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-reveal-first-poll.json b/mobile/rpc-foundation/goldens/sc-reveal-first-poll.json index b4df257bda7..23c77605704 100644 --- a/mobile/rpc-foundation/goldens/sc-reveal-first-poll.json +++ b/mobile/rpc-foundation/goldens/sc-reveal-first-poll.json @@ -3,7 +3,7 @@ "family": "session.tab-reveal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-reveal-timeout.json b/mobile/rpc-foundation/goldens/sc-reveal-timeout.json index 5c91f1cbb99..cda536a1320 100644 --- a/mobile/rpc-foundation/goldens/sc-reveal-timeout.json +++ b/mobile/rpc-foundation/goldens/sc-reveal-timeout.json @@ -3,7 +3,7 @@ "family": "session.tab-reveal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "5e47c7960b9352753f0751d4ccf5e3ff7fb7b74a63cc4e4df3c3ffdcc6c1e66b", diff --git a/mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json b/mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json index 20d345ee3ad..0a4707a76ac 100644 --- a/mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json +++ b/mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.json b/mobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.json index e3301abd1ad..12b86567975 100644 --- a/mobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.json +++ b/mobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-review-commit-rejected.json b/mobile/rpc-foundation/goldens/sc-review-commit-rejected.json index e63cf0b23b9..9d83f1c8e9f 100644 --- a/mobile/rpc-foundation/goldens/sc-review-commit-rejected.json +++ b/mobile/rpc-foundation/goldens/sc-review-commit-rejected.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-review-commit.json b/mobile/rpc-foundation/goldens/sc-review-commit.json index 524741d37d9..9629b49e617 100644 --- a/mobile/rpc-foundation/goldens/sc-review-commit.json +++ b/mobile/rpc-foundation/goldens/sc-review-commit.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-review-status-entries-not-array.json b/mobile/rpc-foundation/goldens/sc-review-status-entries-not-array.json index deff2022d7e..047d9cf7293 100644 --- a/mobile/rpc-foundation/goldens/sc-review-status-entries-not-array.json +++ b/mobile/rpc-foundation/goldens/sc-review-status-entries-not-array.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/sc-review-status-normalized.json b/mobile/rpc-foundation/goldens/sc-review-status-normalized.json index eacaa81418f..23da3a48935 100644 --- a/mobile/rpc-foundation/goldens/sc-review-status-normalized.json +++ b/mobile/rpc-foundation/goldens/sc-review-status-normalized.json @@ -3,7 +3,7 @@ "family": "git.review-preparation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c582ce355b0704b38d7e84cbce352630096c91231bf09aecd1f6dbb409fed17f", diff --git a/mobile/rpc-foundation/goldens/schedules-b3.json b/mobile/rpc-foundation/goldens/schedules-b3.json index 7c9c2183c53..8568e3286db 100644 --- a/mobile/rpc-foundation/goldens/schedules-b3.json +++ b/mobile/rpc-foundation/goldens/schedules-b3.json @@ -3,7 +3,7 @@ "family": "linear-detail-barrier", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.json b/mobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.json index 0dac83a7030..6257be6ebce 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.json b/mobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.json index 3cf50f6238e..fe478fb7b68 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json b/mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json index 779d541b009..8a9385d95c5 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.json b/mobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.json index 9c18239d5bc..902a4feaeee 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.json b/mobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.json index f7e4a6f755f..738e12719a2 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.json b/mobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.json index 63df1d386a8..eb89f74449f 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/session-browser-tab-created.json b/mobile/rpc-foundation/goldens/session-browser-tab-created.json index 2d5e08b1b71..9baae2fb90d 100644 --- a/mobile/rpc-foundation/goldens/session-browser-tab-created.json +++ b/mobile/rpc-foundation/goldens/session-browser-tab-created.json @@ -3,7 +3,7 @@ "family": "session.browser-tab-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-create-browser-refused.json b/mobile/rpc-foundation/goldens/session-create-browser-refused.json index 9983ad3a35b..c96e109c214 100644 --- a/mobile/rpc-foundation/goldens/session-create-browser-refused.json +++ b/mobile/rpc-foundation/goldens/session-create-browser-refused.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-create-browser-tab.json b/mobile/rpc-foundation/goldens/session-create-browser-tab.json index 983b8a25be6..bc5e45cb68c 100644 --- a/mobile/rpc-foundation/goldens/session-create-browser-tab.json +++ b/mobile/rpc-foundation/goldens/session-create-browser-tab.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-create-markdown-name-collision.json b/mobile/rpc-foundation/goldens/session-create-markdown-name-collision.json index c9924c284ac..e9110433925 100644 --- a/mobile/rpc-foundation/goldens/session-create-markdown-name-collision.json +++ b/mobile/rpc-foundation/goldens/session-create-markdown-name-collision.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-create-markdown-note.json b/mobile/rpc-foundation/goldens/session-create-markdown-note.json index 8389c1a288e..539dacb7b29 100644 --- a/mobile/rpc-foundation/goldens/session-create-markdown-note.json +++ b/mobile/rpc-foundation/goldens/session-create-markdown-note.json @@ -3,7 +3,7 @@ "family": "session.content-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-ignores-a-second-create-in-flight.json b/mobile/rpc-foundation/goldens/session-create-terminal-ignores-a-second-create-in-flight.json index 239b59db7b7..4b5a58b5396 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-ignores-a-second-create-in-flight.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-ignores-a-second-create-in-flight.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-launches-an-agent-quick-command.json b/mobile/rpc-foundation/goldens/session-create-terminal-launches-an-agent-quick-command.json index e2073a59e46..a18ed3ff9cc 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-launches-an-agent-quick-command.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-launches-an-agent-quick-command.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-refused.json b/mobile/rpc-foundation/goldens/session-create-terminal-refused.json index ffefbbd3ad7..8fdd4525efc 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-refused.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-refused.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-replaces-active.json b/mobile/rpc-foundation/goldens/session-create-terminal-replaces-active.json index 8761aa773f2..2e102650757 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-replaces-active.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-replaces-active.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-runs-a-quick-command.json b/mobile/rpc-foundation/goldens/session-create-terminal-runs-a-quick-command.json index 447d8b1fd84..32b34ec7df7 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-runs-a-quick-command.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-runs-a-quick-command.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-with-prompt.json b/mobile/rpc-foundation/goldens/session-create-terminal-with-prompt.json index 9227a9bbd26..5d3749fce4a 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-with-prompt.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-with-prompt.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-without-active-tab.json b/mobile/rpc-foundation/goldens/session-create-terminal-without-active-tab.json index 9ac1a36b9f4..d2044ee3466 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-without-active-tab.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-without-active-tab.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-create-terminal-without-handle.json b/mobile/rpc-foundation/goldens/session-create-terminal-without-handle.json index e795bb78dad..a63804fb41f 100644 --- a/mobile/rpc-foundation/goldens/session-create-terminal-without-handle.json +++ b/mobile/rpc-foundation/goldens/session-create-terminal-without-handle.json @@ -3,7 +3,7 @@ "family": "session.create-terminal", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fa7d9fd6428e89282f08e04fefba4289000eb3aed1462489a2f11efed374382c", diff --git a/mobile/rpc-foundation/goldens/session-diff-notes-load-refused.json b/mobile/rpc-foundation/goldens/session-diff-notes-load-refused.json index f7af0614b99..ba5f5442d25 100644 --- a/mobile/rpc-foundation/goldens/session-diff-notes-load-refused.json +++ b/mobile/rpc-foundation/goldens/session-diff-notes-load-refused.json @@ -3,7 +3,7 @@ "family": "session.diff-notes", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/session-diff-notes-loaded.json b/mobile/rpc-foundation/goldens/session-diff-notes-loaded.json index 882b39a8788..6571b445354 100644 --- a/mobile/rpc-foundation/goldens/session-diff-notes-loaded.json +++ b/mobile/rpc-foundation/goldens/session-diff-notes-loaded.json @@ -3,7 +3,7 @@ "family": "session.diff-notes", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/session-file-tab-read.json b/mobile/rpc-foundation/goldens/session-file-tab-read.json index a81e48035cb..397ab8947f0 100644 --- a/mobile/rpc-foundation/goldens/session-file-tab-read.json +++ b/mobile/rpc-foundation/goldens/session-file-tab-read.json @@ -3,7 +3,7 @@ "family": "session.tab-documents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-markdown-disk-read.json b/mobile/rpc-foundation/goldens/session-markdown-disk-read.json index f1aadae09a2..640b9369cbb 100644 --- a/mobile/rpc-foundation/goldens/session-markdown-disk-read.json +++ b/mobile/rpc-foundation/goldens/session-markdown-disk-read.json @@ -3,7 +3,7 @@ "family": "session.markdown-disk-fallback", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-markdown-disk-served.json b/mobile/rpc-foundation/goldens/session-markdown-disk-served.json index 0d93f4221d5..3256e5ee30c 100644 --- a/mobile/rpc-foundation/goldens/session-markdown-disk-served.json +++ b/mobile/rpc-foundation/goldens/session-markdown-disk-served.json @@ -3,7 +3,7 @@ "family": "session.markdown-disk-fallback", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-markdown-save-conflict.json b/mobile/rpc-foundation/goldens/session-markdown-save-conflict.json index ab26ea3f89f..c3bd07ef707 100644 --- a/mobile/rpc-foundation/goldens/session-markdown-save-conflict.json +++ b/mobile/rpc-foundation/goldens/session-markdown-save-conflict.json @@ -3,7 +3,7 @@ "family": "session.markdown-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/session-markdown-saved.json b/mobile/rpc-foundation/goldens/session-markdown-saved.json index 85a6c21322b..1eb556ffcee 100644 --- a/mobile/rpc-foundation/goldens/session-markdown-saved.json +++ b/mobile/rpc-foundation/goldens/session-markdown-saved.json @@ -3,7 +3,7 @@ "family": "session.markdown-save", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "42334358b5e5966001639653b553f15033f6e201d785107871fe056536f0a5e2", diff --git a/mobile/rpc-foundation/goldens/session-markdown-tab-disk-fallback.json b/mobile/rpc-foundation/goldens/session-markdown-tab-disk-fallback.json index 71d1fb90f67..9b7c04268f7 100644 --- a/mobile/rpc-foundation/goldens/session-markdown-tab-disk-fallback.json +++ b/mobile/rpc-foundation/goldens/session-markdown-tab-disk-fallback.json @@ -3,7 +3,7 @@ "family": "session.tab-documents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-markdown-tab-read.json b/mobile/rpc-foundation/goldens/session-markdown-tab-read.json index 658ef42a546..dbbe6e42d48 100644 --- a/mobile/rpc-foundation/goldens/session-markdown-tab-read.json +++ b/mobile/rpc-foundation/goldens/session-markdown-tab-read.json @@ -3,7 +3,7 @@ "family": "session.tab-documents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-markdown-tab-refused.json b/mobile/rpc-foundation/goldens/session-markdown-tab-refused.json index be2359bf3e1..5813d0bbabf 100644 --- a/mobile/rpc-foundation/goldens/session-markdown-tab-refused.json +++ b/mobile/rpc-foundation/goldens/session-markdown-tab-refused.json @@ -3,7 +3,7 @@ "family": "session.tab-documents", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-startup-both-activation-sites.json b/mobile/rpc-foundation/goldens/session-startup-both-activation-sites.json index eba885eb2ab..892d872144e 100644 --- a/mobile/rpc-foundation/goldens/session-startup-both-activation-sites.json +++ b/mobile/rpc-foundation/goldens/session-startup-both-activation-sites.json @@ -3,7 +3,7 @@ "family": "session.startup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "6b08c394e37fd572cf63a4c11934247d379008f11117aa5af33b2926fbd32d1e", diff --git a/mobile/rpc-foundation/goldens/session-startup-floating-route-skips-activation.json b/mobile/rpc-foundation/goldens/session-startup-floating-route-skips-activation.json index 862b1cfadbf..11d41679307 100644 --- a/mobile/rpc-foundation/goldens/session-startup-floating-route-skips-activation.json +++ b/mobile/rpc-foundation/goldens/session-startup-floating-route-skips-activation.json @@ -3,7 +3,7 @@ "family": "session.startup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "6b08c394e37fd572cf63a4c11934247d379008f11117aa5af33b2926fbd32d1e", diff --git a/mobile/rpc-foundation/goldens/session-startup-keeps-terminals-visible-on-reconnect.json b/mobile/rpc-foundation/goldens/session-startup-keeps-terminals-visible-on-reconnect.json index c3ada63e52c..e11e0595888 100644 --- a/mobile/rpc-foundation/goldens/session-startup-keeps-terminals-visible-on-reconnect.json +++ b/mobile/rpc-foundation/goldens/session-startup-keeps-terminals-visible-on-reconnect.json @@ -3,7 +3,7 @@ "family": "session.startup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "6b08c394e37fd572cf63a4c11934247d379008f11117aa5af33b2926fbd32d1e", diff --git a/mobile/rpc-foundation/goldens/session-startup-refused-tab-load-still-loads-terminals.json b/mobile/rpc-foundation/goldens/session-startup-refused-tab-load-still-loads-terminals.json index 5b00f80d5ff..bd939e6fb1e 100644 --- a/mobile/rpc-foundation/goldens/session-startup-refused-tab-load-still-loads-terminals.json +++ b/mobile/rpc-foundation/goldens/session-startup-refused-tab-load-still-loads-terminals.json @@ -3,7 +3,7 @@ "family": "session.startup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "6b08c394e37fd572cf63a4c11934247d379008f11117aa5af33b2926fbd32d1e", diff --git a/mobile/rpc-foundation/goldens/session-tab-activation-focus-and-activate.json b/mobile/rpc-foundation/goldens/session-tab-activation-focus-and-activate.json index 15a12ffc31a..bb06bebaa20 100644 --- a/mobile/rpc-foundation/goldens/session-tab-activation-focus-and-activate.json +++ b/mobile/rpc-foundation/goldens/session-tab-activation-focus-and-activate.json @@ -3,7 +3,7 @@ "family": "session.tab-activation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-tab-activation-refused.json b/mobile/rpc-foundation/goldens/session-tab-activation-refused.json index 8e01f39e9b4..ade387c3881 100644 --- a/mobile/rpc-foundation/goldens/session-tab-activation-refused.json +++ b/mobile/rpc-foundation/goldens/session-tab-activation-refused.json @@ -3,7 +3,7 @@ "family": "session.tab-activation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-tab-activation-transport-error.json b/mobile/rpc-foundation/goldens/session-tab-activation-transport-error.json index 87cf6fd02c8..a9ff3c07061 100644 --- a/mobile/rpc-foundation/goldens/session-tab-activation-transport-error.json +++ b/mobile/rpc-foundation/goldens/session-tab-activation-transport-error.json @@ -3,7 +3,7 @@ "family": "session.tab-activation", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-tab-close-refused-keeps-tab.json b/mobile/rpc-foundation/goldens/session-tab-close-refused-keeps-tab.json index 1a5cd7d5856..a0a9b341926 100644 --- a/mobile/rpc-foundation/goldens/session-tab-close-refused-keeps-tab.json +++ b/mobile/rpc-foundation/goldens/session-tab-close-refused-keeps-tab.json @@ -3,7 +3,7 @@ "family": "session.tab-close", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-tab-close-session-tab.json b/mobile/rpc-foundation/goldens/session-tab-close-session-tab.json index c09669d8612..a8430e54a7e 100644 --- a/mobile/rpc-foundation/goldens/session-tab-close-session-tab.json +++ b/mobile/rpc-foundation/goldens/session-tab-close-session-tab.json @@ -3,7 +3,7 @@ "family": "session.tab-close", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-tab-close-terminal.json b/mobile/rpc-foundation/goldens/session-tab-close-terminal.json index 12ef82b2296..924ce4e9bc1 100644 --- a/mobile/rpc-foundation/goldens/session-tab-close-terminal.json +++ b/mobile/rpc-foundation/goldens/session-tab-close-terminal.json @@ -3,7 +3,7 @@ "family": "session.tab-close", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-tab-closed.json b/mobile/rpc-foundation/goldens/session-tab-closed.json index 5783b081515..6305b14ed17 100644 --- a/mobile/rpc-foundation/goldens/session-tab-closed.json +++ b/mobile/rpc-foundation/goldens/session-tab-closed.json @@ -3,7 +3,7 @@ "family": "session.tab-close-session", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-tab-rename.json b/mobile/rpc-foundation/goldens/session-tab-rename.json index 2feec36471c..b1c45b3bd8b 100644 --- a/mobile/rpc-foundation/goldens/session-tab-rename.json +++ b/mobile/rpc-foundation/goldens/session-tab-rename.json @@ -3,7 +3,7 @@ "family": "session.tab-close", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-tab-renamed.json b/mobile/rpc-foundation/goldens/session-tab-renamed.json index 6993ca54fc3..58b625c326b 100644 --- a/mobile/rpc-foundation/goldens/session-tab-renamed.json +++ b/mobile/rpc-foundation/goldens/session-tab-renamed.json @@ -3,7 +3,7 @@ "family": "session.tab-rename", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "562c2f4ab669e774dd7045ecd9518e483845433fef6034688090f3b056df2815", diff --git a/mobile/rpc-foundation/goldens/session-tabs-health-errored.json b/mobile/rpc-foundation/goldens/session-tabs-health-errored.json index 8b90c593fc5..0b11ce76cd1 100644 --- a/mobile/rpc-foundation/goldens/session-tabs-health-errored.json +++ b/mobile/rpc-foundation/goldens/session-tabs-health-errored.json @@ -3,7 +3,7 @@ "family": "session.tabs-stream-health", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-tabs-health-reconciled.json b/mobile/rpc-foundation/goldens/session-tabs-health-reconciled.json index 75f0c25bde1..9f34d55b1cd 100644 --- a/mobile/rpc-foundation/goldens/session-tabs-health-reconciled.json +++ b/mobile/rpc-foundation/goldens/session-tabs-health-reconciled.json @@ -3,7 +3,7 @@ "family": "session.tabs-stream-health", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-tabs-health-refused.json b/mobile/rpc-foundation/goldens/session-tabs-health-refused.json index aa329965e23..714aa934548 100644 --- a/mobile/rpc-foundation/goldens/session-tabs-health-refused.json +++ b/mobile/rpc-foundation/goldens/session-tabs-health-refused.json @@ -3,7 +3,7 @@ "family": "session.tabs-stream-health", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-tabs-health-stale-application-revision.json b/mobile/rpc-foundation/goldens/session-tabs-health-stale-application-revision.json index 16884a243a6..17d06716ff9 100644 --- a/mobile/rpc-foundation/goldens/session-tabs-health-stale-application-revision.json +++ b/mobile/rpc-foundation/goldens/session-tabs-health-stale-application-revision.json @@ -3,7 +3,7 @@ "family": "session.tabs-stream-health", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4a1e81ab3229c8fd10b3ad435568efec11a944e0f02a183f94e4b1f44a7e5de0", diff --git a/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-take-floor.json b/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-take-floor.json index a9a68e933be..37497a5dc28 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-take-floor.json +++ b/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-take-floor.json @@ -3,7 +3,7 @@ "family": "session.terminal-display-mode", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9e90ad39a8d4257adf30166757a6364c4710dc3ae9f06365f80a3dd8f1c94d89", diff --git a/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-device-token.json b/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-device-token.json index 6d56f11ac61..6d9edfa1bec 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-device-token.json +++ b/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-device-token.json @@ -3,7 +3,7 @@ "family": "session.terminal-display-mode", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9e90ad39a8d4257adf30166757a6364c4710dc3ae9f06365f80a3dd8f1c94d89", diff --git a/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-viewport.json b/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-viewport.json index 1b64db9e382..dfa8f9f824b 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-viewport.json +++ b/mobile/rpc-foundation/goldens/session-terminal-display-mode-auto-without-viewport.json @@ -3,7 +3,7 @@ "family": "session.terminal-display-mode", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9e90ad39a8d4257adf30166757a6364c4710dc3ae9f06365f80a3dd8f1c94d89", diff --git a/mobile/rpc-foundation/goldens/session-terminal-display-mode-drops-second-toggle.json b/mobile/rpc-foundation/goldens/session-terminal-display-mode-drops-second-toggle.json index 1c5903a5d6d..fdfd778559e 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-display-mode-drops-second-toggle.json +++ b/mobile/rpc-foundation/goldens/session-terminal-display-mode-drops-second-toggle.json @@ -3,7 +3,7 @@ "family": "session.terminal-display-mode", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9e90ad39a8d4257adf30166757a6364c4710dc3ae9f06365f80a3dd8f1c94d89", diff --git a/mobile/rpc-foundation/goldens/session-terminal-display-mode-to-desktop.json b/mobile/rpc-foundation/goldens/session-terminal-display-mode-to-desktop.json index 92ff0d69ad8..76621d9b57b 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-display-mode-to-desktop.json +++ b/mobile/rpc-foundation/goldens/session-terminal-display-mode-to-desktop.json @@ -3,7 +3,7 @@ "family": "session.terminal-display-mode", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9e90ad39a8d4257adf30166757a6364c4710dc3ae9f06365f80a3dd8f1c94d89", diff --git a/mobile/rpc-foundation/goldens/session-terminal-list-dedupes-handles.json b/mobile/rpc-foundation/goldens/session-terminal-list-dedupes-handles.json index ce2c981fcc6..3e579906856 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-list-dedupes-handles.json +++ b/mobile/rpc-foundation/goldens/session-terminal-list-dedupes-handles.json @@ -3,7 +3,7 @@ "family": "session.terminal-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-terminal-list-empty-guarded.json b/mobile/rpc-foundation/goldens/session-terminal-list-empty-guarded.json index bc67e4d906b..8411ce561af 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-list-empty-guarded.json +++ b/mobile/rpc-foundation/goldens/session-terminal-list-empty-guarded.json @@ -3,7 +3,7 @@ "family": "session.terminal-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-terminal-list-merged.json b/mobile/rpc-foundation/goldens/session-terminal-list-merged.json index ae2400ee3fb..af1bbc81802 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-list-merged.json +++ b/mobile/rpc-foundation/goldens/session-terminal-list-merged.json @@ -3,7 +3,7 @@ "family": "session.terminal-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/session-terminal-list-refused.json b/mobile/rpc-foundation/goldens/session-terminal-list-refused.json index facb44a2774..d4d5a7ba927 100644 --- a/mobile/rpc-foundation/goldens/session-terminal-list-refused.json +++ b/mobile/rpc-foundation/goldens/session-terminal-list-refused.json @@ -3,7 +3,7 @@ "family": "session.terminal-inventory", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "a3daf9a05d05424959baf77feb45cb0c076d77d8c49e877941ed612cc1e5ce95", diff --git a/mobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.json b/mobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.json index 076ef068215..4f29c57f237 100644 --- a/mobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json b/mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json index c3165ecc277..60ab89a9660 100644 --- a/mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json +++ b/mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-bot-overrides-refused.json b/mobile/rpc-foundation/goldens/settings-bot-overrides-refused.json index a1c85cbd138..cd1fe52a77c 100644 --- a/mobile/rpc-foundation/goldens/settings-bot-overrides-refused.json +++ b/mobile/rpc-foundation/goldens/settings-bot-overrides-refused.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.json b/mobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.json index 3488d08cd40..a9583652cc0 100644 --- a/mobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.bot-overrides", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-home-coalesced.json b/mobile/rpc-foundation/goldens/settings-home-coalesced.json index dc69d3ffa45..19407ee70dd 100644 --- a/mobile/rpc-foundation/goldens/settings-home-coalesced.json +++ b/mobile/rpc-foundation/goldens/settings-home-coalesced.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-home-providers-fulfilled.json b/mobile/rpc-foundation/goldens/settings-home-providers-fulfilled.json index 1415f0dfe4b..d35a57ab17a 100644 --- a/mobile/rpc-foundation/goldens/settings-home-providers-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-home-providers-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json b/mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json index d2c9191234f..dbeed96cca4 100644 --- a/mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json +++ b/mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-home-providers-refused.json b/mobile/rpc-foundation/goldens/settings-home-providers-refused.json index 3c87c93dd28..f1b19ad17bf 100644 --- a/mobile/rpc-foundation/goldens/settings-home-providers-refused.json +++ b/mobile/rpc-foundation/goldens/settings-home-providers-refused.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-home-providers-transport-error.json b/mobile/rpc-foundation/goldens/settings-home-providers-transport-error.json index bde81da5c50..25d2207a07c 100644 --- a/mobile/rpc-foundation/goldens/settings-home-providers-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-home-providers-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.home-providers", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-new-tab-refused.json b/mobile/rpc-foundation/goldens/settings-new-tab-refused.json index 6d4fa59273c..d6385cce795 100644 --- a/mobile/rpc-foundation/goldens/settings-new-tab-refused.json +++ b/mobile/rpc-foundation/goldens/settings-new-tab-refused.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/settings-new-tab-ssh.json b/mobile/rpc-foundation/goldens/settings-new-tab-ssh.json index efa898d4e1c..2ba29ec2f2c 100644 --- a/mobile/rpc-foundation/goldens/settings-new-tab-ssh.json +++ b/mobile/rpc-foundation/goldens/settings-new-tab-ssh.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json b/mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json index 3aa65cfa1bf..1c3c263ae7e 100644 --- a/mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json @@ -3,7 +3,7 @@ "family": "settings-agent-read", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "448cdbd12f4f6a14bb33947bfbbb1837aebeb28979db70c62f2ba2fbb4d89c8f", diff --git a/mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json b/mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json index d37e94181b2..f5a5b48b1b3 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json +++ b/mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json index 111b6a14dfe..6b1a6348154 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json index 6b6ae3ee351..ae1783278a5 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json index 0fc7ff78880..f9ed435f43d 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json index 54a3384af19..6b339e7bf4e 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json index 17253bc69bb..62ef1dd5710 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json index 84ce6519066..9eef882d2c3 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.repo-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json b/mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json index eb7225424c7..e09dcfe7d7c 100644 --- a/mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json b/mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json index e2eae5d35ff..0ee5dcf3b65 100644 --- a/mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json +++ b/mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-resume-metadata-refused.json b/mobile/rpc-foundation/goldens/settings-resume-metadata-refused.json index 3a6b0588859..d433d72035d 100644 --- a/mobile/rpc-foundation/goldens/settings-resume-metadata-refused.json +++ b/mobile/rpc-foundation/goldens/settings-resume-metadata-refused.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.json b/mobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.json index d6f480e990d..3578ea196cc 100644 --- a/mobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.resume-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.json b/mobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.json index 2e465e0567b..4b271b141f0 100644 --- a/mobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json b/mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json index 12f1e31f9ba..e66deccd577 100644 --- a/mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json +++ b/mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-task-hydration-refused.json b/mobile/rpc-foundation/goldens/settings-task-hydration-refused.json index d4898ec62ed..69df8315f97 100644 --- a/mobile/rpc-foundation/goldens/settings-task-hydration-refused.json +++ b/mobile/rpc-foundation/goldens/settings-task-hydration-refused.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json b/mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json index e5cf7018607..863438ba762 100644 --- a/mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.task-hydration", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-task-workspace-create-linear.json b/mobile/rpc-foundation/goldens/settings-task-workspace-create-linear.json index de903acfe74..210aa3d107f 100644 --- a/mobile/rpc-foundation/goldens/settings-task-workspace-create-linear.json +++ b/mobile/rpc-foundation/goldens/settings-task-workspace-create-linear.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json b/mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json index 0bb54e80434..66ed6d4865e 100644 --- a/mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json +++ b/mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.json b/mobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.json index f21675f15e4..3fb03be495a 100644 --- a/mobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-task-workspace-refused.json b/mobile/rpc-foundation/goldens/settings-task-workspace-refused.json index e6f94ef89fe..2e4818627e6 100644 --- a/mobile/rpc-foundation/goldens/settings-task-workspace-refused.json +++ b/mobile/rpc-foundation/goldens/settings-task-workspace-refused.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-task-workspace-transport-error.json b/mobile/rpc-foundation/goldens/settings-task-workspace-transport-error.json index 25c1677363c..f9f05142440 100644 --- a/mobile/rpc-foundation/goldens/settings-task-workspace-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-task-workspace-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.task-workspace", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-task-write.json b/mobile/rpc-foundation/goldens/settings-task-write.json index 8e3b1344e80..44657753f03 100644 --- a/mobile/rpc-foundation/goldens/settings-task-write.json +++ b/mobile/rpc-foundation/goldens/settings-task-write.json @@ -3,7 +3,7 @@ "family": "settings-best-effort", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.json b/mobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.json index 36b613e9781..d57da179268 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json b/mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json index c96f4053db0..030bd73b2e0 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-context-refused.json b/mobile/rpc-foundation/goldens/settings-workspace-context-refused.json index 9de8120f48d..f2efcefee29 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-context-refused.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-context-refused.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-context-transport-error.json b/mobile/rpc-foundation/goldens/settings-workspace-context-transport-error.json index d74686c598c..cd3f9d80d9b 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-context-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-context-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.workspace-context", "namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "fc73116d95985606fe32f22d3f14f434a6d5cd49219416c9fa0f47f971fa6ff0", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.json b/mobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.json index 1e267d21844..84747858014 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.json @@ -3,7 +3,7 @@ "family": "settings.workspace-submit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-submit-refused.json b/mobile/rpc-foundation/goldens/settings-workspace-submit-refused.json index 940ae10a1d6..201f7a6c7f4 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-submit-refused.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-submit-refused.json @@ -3,7 +3,7 @@ "family": "settings.workspace-submit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.json b/mobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.json index 93bd4037bd9..481b25bab50 100644 --- a/mobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.json @@ -3,7 +3,7 @@ "family": "settings.workspace-submit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c2eed306311a844cd6f2f84b6513c0a1182f86a5e3cace434385b8287c80d7c5", diff --git a/mobile/rpc-foundation/goldens/speech-audio-chunk-acknowledged.json b/mobile/rpc-foundation/goldens/speech-audio-chunk-acknowledged.json index f3485664c9e..0713771ad5d 100644 --- a/mobile/rpc-foundation/goldens/speech-audio-chunk-acknowledged.json +++ b/mobile/rpc-foundation/goldens/speech-audio-chunk-acknowledged.json @@ -3,7 +3,7 @@ "family": "speech.dictation-chunk", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-desktop-start-fulfilled.json b/mobile/rpc-foundation/goldens/speech-desktop-start-fulfilled.json index 4d33e3c624b..9153cc6ecf4 100644 --- a/mobile/rpc-foundation/goldens/speech-desktop-start-fulfilled.json +++ b/mobile/rpc-foundation/goldens/speech-desktop-start-fulfilled.json @@ -3,7 +3,7 @@ "family": "speech.dictation-start", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-desktop-start-recording-failed.json b/mobile/rpc-foundation/goldens/speech-desktop-start-recording-failed.json index f2831b7636c..b6414ad3be4 100644 --- a/mobile/rpc-foundation/goldens/speech-desktop-start-recording-failed.json +++ b/mobile/rpc-foundation/goldens/speech-desktop-start-recording-failed.json @@ -3,7 +3,7 @@ "family": "speech.dictation-start", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-desktop-start-superseded.json b/mobile/rpc-foundation/goldens/speech-desktop-start-superseded.json index d02d6216dfe..0ecb2748b23 100644 --- a/mobile/rpc-foundation/goldens/speech-desktop-start-superseded.json +++ b/mobile/rpc-foundation/goldens/speech-desktop-start-superseded.json @@ -3,7 +3,7 @@ "family": "speech.dictation-start", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-dictation-session-cancelled.json b/mobile/rpc-foundation/goldens/speech-dictation-session-cancelled.json index d047633cb91..125eb6bc371 100644 --- a/mobile/rpc-foundation/goldens/speech-dictation-session-cancelled.json +++ b/mobile/rpc-foundation/goldens/speech-dictation-session-cancelled.json @@ -3,7 +3,7 @@ "family": "speech.dictation-session", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-dictation-session-transcript.json b/mobile/rpc-foundation/goldens/speech-dictation-session-transcript.json index 0e1c06d05f8..431cdd64901 100644 --- a/mobile/rpc-foundation/goldens/speech-dictation-session-transcript.json +++ b/mobile/rpc-foundation/goldens/speech-dictation-session-transcript.json @@ -3,7 +3,7 @@ "family": "speech.dictation-session", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-setup-sheet-denied-to-mobile.json b/mobile/rpc-foundation/goldens/speech-setup-sheet-denied-to-mobile.json index 73d8a6b1204..9da4f838ded 100644 --- a/mobile/rpc-foundation/goldens/speech-setup-sheet-denied-to-mobile.json +++ b/mobile/rpc-foundation/goldens/speech-setup-sheet-denied-to-mobile.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-setup-sheet-fulfilled.json b/mobile/rpc-foundation/goldens/speech-setup-sheet-fulfilled.json index d93d72fba04..0787dde585f 100644 --- a/mobile/rpc-foundation/goldens/speech-setup-sheet-fulfilled.json +++ b/mobile/rpc-foundation/goldens/speech-setup-sheet-fulfilled.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-setup-sheet-legacy-desktop.json b/mobile/rpc-foundation/goldens/speech-setup-sheet-legacy-desktop.json index 66295c6e144..b5ce7d1ec5d 100644 --- a/mobile/rpc-foundation/goldens/speech-setup-sheet-legacy-desktop.json +++ b/mobile/rpc-foundation/goldens/speech-setup-sheet-legacy-desktop.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/speech-setup-sheet-model-vocabulary.json b/mobile/rpc-foundation/goldens/speech-setup-sheet-model-vocabulary.json index 53be7509cc4..fe546b7898c 100644 --- a/mobile/rpc-foundation/goldens/speech-setup-sheet-model-vocabulary.json +++ b/mobile/rpc-foundation/goldens/speech-setup-sheet-model-vocabulary.json @@ -3,7 +3,7 @@ "family": "speech.setup-sheet", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "3ddb44511547ae2fc97340e5a49393233f6716f9704b7b994f43bc030788b25b", diff --git a/mobile/rpc-foundation/goldens/structured-agent-session-created.json b/mobile/rpc-foundation/goldens/structured-agent-session-created.json index 6819729a742..c2ed99d4499 100644 --- a/mobile/rpc-foundation/goldens/structured-agent-session-created.json +++ b/mobile/rpc-foundation/goldens/structured-agent-session-created.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/structured-launch-created.json b/mobile/rpc-foundation/goldens/structured-launch-created.json index 894db47ff14..f13e802cbb6 100644 --- a/mobile/rpc-foundation/goldens/structured-launch-created.json +++ b/mobile/rpc-foundation/goldens/structured-launch-created.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/structured-launch-definitive-refusal.json b/mobile/rpc-foundation/goldens/structured-launch-definitive-refusal.json index 236da473d8c..3e2024f42a9 100644 --- a/mobile/rpc-foundation/goldens/structured-launch-definitive-refusal.json +++ b/mobile/rpc-foundation/goldens/structured-launch-definitive-refusal.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/structured-launch-replays-dropped-create.json b/mobile/rpc-foundation/goldens/structured-launch-replays-dropped-create.json index f25a46301ee..7e155fde891 100644 --- a/mobile/rpc-foundation/goldens/structured-launch-replays-dropped-create.json +++ b/mobile/rpc-foundation/goldens/structured-launch-replays-dropped-create.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/structured-launch-support-refused.json b/mobile/rpc-foundation/goldens/structured-launch-support-refused.json index e5ced5c2a32..e821c7cea4f 100644 --- a/mobile/rpc-foundation/goldens/structured-launch-support-refused.json +++ b/mobile/rpc-foundation/goldens/structured-launch-support-refused.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/structured-launch-unsupported.json b/mobile/rpc-foundation/goldens/structured-launch-unsupported.json index ccf14b0dfd9..fca717c6f28 100644 --- a/mobile/rpc-foundation/goldens/structured-launch-unsupported.json +++ b/mobile/rpc-foundation/goldens/structured-launch-unsupported.json @@ -3,7 +3,7 @@ "family": "agentSession.structured-launch", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d340697a64a198066b1037a550afb9a0de7507246a545901d2fc0a23407f38d6", diff --git a/mobile/rpc-foundation/goldens/tasks-route-repo-list.json b/mobile/rpc-foundation/goldens/tasks-route-repo-list.json index f7a50b64545..6881876182c 100644 --- a/mobile/rpc-foundation/goldens/tasks-route-repo-list.json +++ b/mobile/rpc-foundation/goldens/tasks-route-repo-list.json @@ -3,7 +3,7 @@ "family": "tasks.route-repo-list", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "feb6cee1ab7ecff1ba98bfba22d4924c748d3bb6b749db460cb617ee50b92f2c", diff --git a/mobile/rpc-foundation/goldens/terminal-gesture-flush-and-clear.json b/mobile/rpc-foundation/goldens/terminal-gesture-flush-and-clear.json index a0346c30bc3..60a9844b2ff 100644 --- a/mobile/rpc-foundation/goldens/terminal-gesture-flush-and-clear.json +++ b/mobile/rpc-foundation/goldens/terminal-gesture-flush-and-clear.json @@ -3,7 +3,7 @@ "family": "session.terminal-gesture-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "9d119d5ec320e2538105d6ff673b9f4b8e3decbe46527947489dffbcf2ac0472", diff --git a/mobile/rpc-foundation/goldens/terminal-input-send-accepted.json b/mobile/rpc-foundation/goldens/terminal-input-send-accepted.json index f59bc417dcb..d045b82ec56 100644 --- a/mobile/rpc-foundation/goldens/terminal-input-send-accepted.json +++ b/mobile/rpc-foundation/goldens/terminal-input-send-accepted.json @@ -3,7 +3,7 @@ "family": "session.terminal-input-send", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/terminal-input-send-refused.json b/mobile/rpc-foundation/goldens/terminal-input-send-refused.json index 01856912c34..aeb64e780ef 100644 --- a/mobile/rpc-foundation/goldens/terminal-input-send-refused.json +++ b/mobile/rpc-foundation/goldens/terminal-input-send-refused.json @@ -3,7 +3,7 @@ "family": "session.terminal-input-send", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/terminal-live-input-accepted.json b/mobile/rpc-foundation/goldens/terminal-live-input-accepted.json index cfdef1b78ef..0a6205e6809 100644 --- a/mobile/rpc-foundation/goldens/terminal-live-input-accepted.json +++ b/mobile/rpc-foundation/goldens/terminal-live-input-accepted.json @@ -3,7 +3,7 @@ "family": "session.terminal-input-send", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/terminal-paste-accepted.json b/mobile/rpc-foundation/goldens/terminal-paste-accepted.json index 19681a7278f..5a4c3c39d3e 100644 --- a/mobile/rpc-foundation/goldens/terminal-paste-accepted.json +++ b/mobile/rpc-foundation/goldens/terminal-paste-accepted.json @@ -3,7 +3,7 @@ "family": "session.terminal-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/terminal-paste-refused.json b/mobile/rpc-foundation/goldens/terminal-paste-refused.json index 800bf304671..bfa4c1243a7 100644 --- a/mobile/rpc-foundation/goldens/terminal-paste-refused.json +++ b/mobile/rpc-foundation/goldens/terminal-paste-refused.json @@ -3,7 +3,7 @@ "family": "session.terminal-paste", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/terminal-query-reply-accepted.json b/mobile/rpc-foundation/goldens/terminal-query-reply-accepted.json index d5736625dd0..ed59187ec69 100644 --- a/mobile/rpc-foundation/goldens/terminal-query-reply-accepted.json +++ b/mobile/rpc-foundation/goldens/terminal-query-reply-accepted.json @@ -3,7 +3,7 @@ "family": "terminal.query-reply", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-query-reply-unsubscribed.json b/mobile/rpc-foundation/goldens/terminal-query-reply-unsubscribed.json index befacc36e80..c1cb734d890 100644 --- a/mobile/rpc-foundation/goldens/terminal-query-reply-unsubscribed.json +++ b/mobile/rpc-foundation/goldens/terminal-query-reply-unsubscribed.json @@ -3,7 +3,7 @@ "family": "terminal.query-reply", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-raw-input-refused.json b/mobile/rpc-foundation/goldens/terminal-raw-input-refused.json index e6144ef92b0..dbf28103ca6 100644 --- a/mobile/rpc-foundation/goldens/terminal-raw-input-refused.json +++ b/mobile/rpc-foundation/goldens/terminal-raw-input-refused.json @@ -3,7 +3,7 @@ "family": "terminal.raw-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-raw-input-reported.json b/mobile/rpc-foundation/goldens/terminal-raw-input-reported.json index f0591e491be..6f559f0d86b 100644 --- a/mobile/rpc-foundation/goldens/terminal-raw-input-reported.json +++ b/mobile/rpc-foundation/goldens/terminal-raw-input-reported.json @@ -3,7 +3,7 @@ "family": "terminal.raw-input", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-takeover-report-accepted.json b/mobile/rpc-foundation/goldens/terminal-takeover-report-accepted.json index b274441c2b4..84cd3f18a6c 100644 --- a/mobile/rpc-foundation/goldens/terminal-takeover-report-accepted.json +++ b/mobile/rpc-foundation/goldens/terminal-takeover-report-accepted.json @@ -3,7 +3,7 @@ "family": "terminal.takeover-report", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-takeover-report-retried.json b/mobile/rpc-foundation/goldens/terminal-takeover-report-retried.json index 9c1e209ecf8..618b01659b4 100644 --- a/mobile/rpc-foundation/goldens/terminal-takeover-report-retried.json +++ b/mobile/rpc-foundation/goldens/terminal-takeover-report-retried.json @@ -3,7 +3,7 @@ "family": "terminal.takeover-report", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-viewport-refit-applied.json b/mobile/rpc-foundation/goldens/terminal-viewport-refit-applied.json index 9d188730755..8e5a75b6eda 100644 --- a/mobile/rpc-foundation/goldens/terminal-viewport-refit-applied.json +++ b/mobile/rpc-foundation/goldens/terminal-viewport-refit-applied.json @@ -3,7 +3,7 @@ "family": "terminal.viewport-refit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-viewport-refit-legacy-desktop.json b/mobile/rpc-foundation/goldens/terminal-viewport-refit-legacy-desktop.json index d82614cdd89..e8e22d001f1 100644 --- a/mobile/rpc-foundation/goldens/terminal-viewport-refit-legacy-desktop.json +++ b/mobile/rpc-foundation/goldens/terminal-viewport-refit-legacy-desktop.json @@ -3,7 +3,7 @@ "family": "terminal.viewport-refit", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "7588d30f33a8bb846c48f160aa9a4a8138176662bb2fb6be7bbdf352f553d05f", diff --git a/mobile/rpc-foundation/goldens/terminal-worktree-connection-resolved.json b/mobile/rpc-foundation/goldens/terminal-worktree-connection-resolved.json index e5369047767..dd2bcf06bc1 100644 --- a/mobile/rpc-foundation/goldens/terminal-worktree-connection-resolved.json +++ b/mobile/rpc-foundation/goldens/terminal-worktree-connection-resolved.json @@ -3,7 +3,7 @@ "family": "session.worktree-connection", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "e7e3718f685e3713cf1b8209d59d618741f892bd286853b79bb456c59cec8d86", diff --git a/mobile/rpc-foundation/goldens/tk-create-github.json b/mobile/rpc-foundation/goldens/tk-create-github.json index 12c6382c67f..2a0814e1c1e 100644 --- a/mobile/rpc-foundation/goldens/tk-create-github.json +++ b/mobile/rpc-foundation/goldens/tk-create-github.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-create-gitlab.json b/mobile/rpc-foundation/goldens/tk-create-gitlab.json index 0204f27b9ab..84728f80ac4 100644 --- a/mobile/rpc-foundation/goldens/tk-create-gitlab.json +++ b/mobile/rpc-foundation/goldens/tk-create-gitlab.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-create-linear.json b/mobile/rpc-foundation/goldens/tk-create-linear.json index 840fc84b802..f862b2069a4 100644 --- a/mobile/rpc-foundation/goldens/tk-create-linear.json +++ b/mobile/rpc-foundation/goldens/tk-create-linear.json @@ -3,7 +3,7 @@ "family": "tasks.task-create-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-item-checks-files.json b/mobile/rpc-foundation/goldens/tk-item-checks-files.json index 96f0e05cb2c..37142986100 100644 --- a/mobile/rpc-foundation/goldens/tk-item-checks-files.json +++ b/mobile/rpc-foundation/goldens/tk-item-checks-files.json @@ -3,7 +3,7 @@ "family": "tasks.item-checks-files", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/tk-item-comment-github.json b/mobile/rpc-foundation/goldens/tk-item-comment-github.json index 602c974130f..63e879d4776 100644 --- a/mobile/rpc-foundation/goldens/tk-item-comment-github.json +++ b/mobile/rpc-foundation/goldens/tk-item-comment-github.json @@ -3,7 +3,7 @@ "family": "tasks.item-comment-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/tk-item-comment-gitlab-mr.json b/mobile/rpc-foundation/goldens/tk-item-comment-gitlab-mr.json index f8c3b17dc35..237b9074831 100644 --- a/mobile/rpc-foundation/goldens/tk-item-comment-gitlab-mr.json +++ b/mobile/rpc-foundation/goldens/tk-item-comment-gitlab-mr.json @@ -3,7 +3,7 @@ "family": "tasks.item-comment-gitlab-mr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/tk-item-comment-gitlab.json b/mobile/rpc-foundation/goldens/tk-item-comment-gitlab.json index c36a59ddca8..7a4eeef225c 100644 --- a/mobile/rpc-foundation/goldens/tk-item-comment-gitlab.json +++ b/mobile/rpc-foundation/goldens/tk-item-comment-gitlab.json @@ -3,7 +3,7 @@ "family": "tasks.item-comment-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/tk-item-detail-github-reactions.json b/mobile/rpc-foundation/goldens/tk-item-detail-github-reactions.json index 4d5b7067c5d..f3f6789103e 100644 --- a/mobile/rpc-foundation/goldens/tk-item-detail-github-reactions.json +++ b/mobile/rpc-foundation/goldens/tk-item-detail-github-reactions.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/tk-item-detail-github.json b/mobile/rpc-foundation/goldens/tk-item-detail-github.json index 1ea99b3c0aa..b4a8dab1025 100644 --- a/mobile/rpc-foundation/goldens/tk-item-detail-github.json +++ b/mobile/rpc-foundation/goldens/tk-item-detail-github.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/tk-item-detail-gitlab-reactions.json b/mobile/rpc-foundation/goldens/tk-item-detail-gitlab-reactions.json index 30bb82d10bb..ba2eafc1141 100644 --- a/mobile/rpc-foundation/goldens/tk-item-detail-gitlab-reactions.json +++ b/mobile/rpc-foundation/goldens/tk-item-detail-gitlab-reactions.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/tk-item-detail-gitlab.json b/mobile/rpc-foundation/goldens/tk-item-detail-gitlab.json index 16fc27a5e93..63343def5a0 100644 --- a/mobile/rpc-foundation/goldens/tk-item-detail-gitlab.json +++ b/mobile/rpc-foundation/goldens/tk-item-detail-gitlab.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/tk-item-detail-linear.json b/mobile/rpc-foundation/goldens/tk-item-detail-linear.json index 6c373d1186d..95ab430abc1 100644 --- a/mobile/rpc-foundation/goldens/tk-item-detail-linear.json +++ b/mobile/rpc-foundation/goldens/tk-item-detail-linear.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c0ef16b959002e4a3c5347114a0844b95670e274ef010d910b6671ac5f49e783", diff --git a/mobile/rpc-foundation/goldens/tk-item-detail-metadata.json b/mobile/rpc-foundation/goldens/tk-item-detail-metadata.json index b3af1259c82..6107483b462 100644 --- a/mobile/rpc-foundation/goldens/tk-item-detail-metadata.json +++ b/mobile/rpc-foundation/goldens/tk-item-detail-metadata.json @@ -3,7 +3,7 @@ "family": "tasks.item-detail-metadata", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "58ea1553e04017c993aea4753aace41ee664705a3fdb3b18569c5a9d7968cf06", diff --git a/mobile/rpc-foundation/goldens/tk-item-merge-gitlab.json b/mobile/rpc-foundation/goldens/tk-item-merge-gitlab.json index fa99d7d3d72..3cf732738b8 100644 --- a/mobile/rpc-foundation/goldens/tk-item-merge-gitlab.json +++ b/mobile/rpc-foundation/goldens/tk-item-merge-gitlab.json @@ -3,7 +3,7 @@ "family": "tasks.item-merge-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/tk-item-metadata-github.json b/mobile/rpc-foundation/goldens/tk-item-metadata-github.json index dd2efda3fbf..3436eaf8dd2 100644 --- a/mobile/rpc-foundation/goldens/tk-item-metadata-github.json +++ b/mobile/rpc-foundation/goldens/tk-item-metadata-github.json @@ -3,7 +3,7 @@ "family": "tasks.item-metadata-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab-mr.json b/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab-mr.json index e38436d89b3..432b2a45d93 100644 --- a/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab-mr.json +++ b/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab-mr.json @@ -3,7 +3,7 @@ "family": "tasks.item-metadata-gitlab-mr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab.json b/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab.json index f783f2bf635..4612780b14b 100644 --- a/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab.json +++ b/mobile/rpc-foundation/goldens/tk-item-metadata-gitlab.json @@ -3,7 +3,7 @@ "family": "tasks.item-metadata-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/tk-item-reply-merge.json b/mobile/rpc-foundation/goldens/tk-item-reply-merge.json index 78030dba264..45da471c532 100644 --- a/mobile/rpc-foundation/goldens/tk-item-reply-merge.json +++ b/mobile/rpc-foundation/goldens/tk-item-reply-merge.json @@ -3,7 +3,7 @@ "family": "tasks.item-reply-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/tk-item-review-github.json b/mobile/rpc-foundation/goldens/tk-item-review-github.json index b1192ff8fb1..86a57193aa6 100644 --- a/mobile/rpc-foundation/goldens/tk-item-review-github.json +++ b/mobile/rpc-foundation/goldens/tk-item-review-github.json @@ -3,7 +3,7 @@ "family": "tasks.item-review-github", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8f68885d57a9aa76d80ba0ee29a95bdbaa98cef29c79c68ce75d67202cde7bfe", diff --git a/mobile/rpc-foundation/goldens/tk-item-status-gitlab-mr.json b/mobile/rpc-foundation/goldens/tk-item-status-gitlab-mr.json index d1f463a9994..8c17643e3ac 100644 --- a/mobile/rpc-foundation/goldens/tk-item-status-gitlab-mr.json +++ b/mobile/rpc-foundation/goldens/tk-item-status-gitlab-mr.json @@ -3,7 +3,7 @@ "family": "tasks.item-status-gitlab-mr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/tk-item-status-gitlab.json b/mobile/rpc-foundation/goldens/tk-item-status-gitlab.json index 05bb0387600..6cc6c89d157 100644 --- a/mobile/rpc-foundation/goldens/tk-item-status-gitlab.json +++ b/mobile/rpc-foundation/goldens/tk-item-status-gitlab.json @@ -3,7 +3,7 @@ "family": "tasks.item-status-gitlab", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "8c4218bfb2af227da5386f29989cec438f2c6187f39ce1c06859e136ea920bfa", diff --git a/mobile/rpc-foundation/goldens/tk-linear-connect.json b/mobile/rpc-foundation/goldens/tk-linear-connect.json index 5ad3af0b71b..c4672b6ec87 100644 --- a/mobile/rpc-foundation/goldens/tk-linear-connect.json +++ b/mobile/rpc-foundation/goldens/tk-linear-connect.json @@ -3,7 +3,7 @@ "family": "tasks.linear-connect", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-linear-item.json b/mobile/rpc-foundation/goldens/tk-linear-item.json index cc9bbf5030d..69996b58d71 100644 --- a/mobile/rpc-foundation/goldens/tk-linear-item.json +++ b/mobile/rpc-foundation/goldens/tk-linear-item.json @@ -3,7 +3,7 @@ "family": "tasks.linear-item", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "97cfbcd82778ed6517ca2d10b2f3ad5a8d366e380d7846c1e89d5a5baf17e739", diff --git a/mobile/rpc-foundation/goldens/tk-linear-team-context.json b/mobile/rpc-foundation/goldens/tk-linear-team-context.json index 496ec532e4b..6d70a279884 100644 --- a/mobile/rpc-foundation/goldens/tk-linear-team-context.json +++ b/mobile/rpc-foundation/goldens/tk-linear-team-context.json @@ -3,7 +3,7 @@ "family": "tasks.linear-team-context", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "58ea1553e04017c993aea4753aace41ee664705a3fdb3b18569c5a9d7968cf06", diff --git a/mobile/rpc-foundation/goldens/tk-list-gitlab-items.json b/mobile/rpc-foundation/goldens/tk-list-gitlab-items.json index 780952b3a8e..50f6c4f8a9e 100644 --- a/mobile/rpc-foundation/goldens/tk-list-gitlab-items.json +++ b/mobile/rpc-foundation/goldens/tk-list-gitlab-items.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-gitlab-items", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-list-gitlab-todos.json b/mobile/rpc-foundation/goldens/tk-list-gitlab-todos.json index 7b08c3b82dd..911f89be5c4 100644 --- a/mobile/rpc-foundation/goldens/tk-list-gitlab-todos.json +++ b/mobile/rpc-foundation/goldens/tk-list-gitlab-todos.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-gitlab-todos", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-list-linear.json b/mobile/rpc-foundation/goldens/tk-list-linear.json index b864dea7f23..2b2082832fc 100644 --- a/mobile/rpc-foundation/goldens/tk-list-linear.json +++ b/mobile/rpc-foundation/goldens/tk-list-linear.json @@ -3,7 +3,7 @@ "family": "tasks.task-list-linear", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/tk-project-board-load.json b/mobile/rpc-foundation/goldens/tk-project-board-load.json index 444f0417abb..d71136bc311 100644 --- a/mobile/rpc-foundation/goldens/tk-project-board-load.json +++ b/mobile/rpc-foundation/goldens/tk-project-board-load.json @@ -3,7 +3,7 @@ "family": "tasks.project-board-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/tk-project-repo-slugs.json b/mobile/rpc-foundation/goldens/tk-project-repo-slugs.json index 33beadc63be..b0f0caaa60a 100644 --- a/mobile/rpc-foundation/goldens/tk-project-repo-slugs.json +++ b/mobile/rpc-foundation/goldens/tk-project-repo-slugs.json @@ -3,7 +3,7 @@ "family": "tasks.project-repo-slugs", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "c4272385ed3b0de4feab38de9e4f6363ecd6317fdd4de47f76a98eb18abaf371", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-comments-issue.json b/mobile/rpc-foundation/goldens/tk-project-row-comments-issue.json index 39d021edf51..6dae58f6c42 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-comments-issue.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-comments-issue.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-comments-issue", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-comments-pr.json b/mobile/rpc-foundation/goldens/tk-project-row-comments-pr.json index 18c903f9b78..8c57208f6c2 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-comments-pr.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-comments-pr.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-comments-pr", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-detail.json b/mobile/rpc-foundation/goldens/tk-project-row-detail.json index 44013321640..91caf6cbbaf 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-detail.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-detail.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-detail", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f8f6e5d500f959b9b15c5498885a05422747880b6aef4ad795bc3064ebbacea6", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-fields.json b/mobile/rpc-foundation/goldens/tk-project-row-fields.json index da7e022518e..00ee513d05e 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-fields.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-fields.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-fields", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-files-merge.json b/mobile/rpc-foundation/goldens/tk-project-row-files-merge.json index d88823231c4..3fdf6e0d389 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-files-merge.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-files-merge.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-files-merge", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "b228732762828412ad3d9eec3ece00a897d866046e37044322c3911758d6e0a9", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-metadata-load.json b/mobile/rpc-foundation/goldens/tk-project-row-metadata-load.json index 0696ec08cda..b816e75c56f 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-metadata-load.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-metadata-load.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-metadata-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "f8f6e5d500f959b9b15c5498885a05422747880b6aef4ad795bc3064ebbacea6", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-review-checks.json b/mobile/rpc-foundation/goldens/tk-project-row-review-checks.json index d1886375eb1..7600a0022bc 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-review-checks.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-review-checks.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-review-checks", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "370aeaee59978071ccb821da13c9e6114936c168947b608539cdb80d40cc9889", diff --git a/mobile/rpc-foundation/goldens/tk-project-row-threads.json b/mobile/rpc-foundation/goldens/tk-project-row-threads.json index 46a7b6d014e..cd4304bd421 100644 --- a/mobile/rpc-foundation/goldens/tk-project-row-threads.json +++ b/mobile/rpc-foundation/goldens/tk-project-row-threads.json @@ -3,7 +3,7 @@ "family": "tasks.project-row-threads", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "55058202df36c8b951510215936e496ea88d3d71a6690090a13c52deb13e34e1", diff --git a/mobile/rpc-foundation/goldens/tk-provider-load.json b/mobile/rpc-foundation/goldens/tk-provider-load.json index 3869305b568..d8f207d1e63 100644 --- a/mobile/rpc-foundation/goldens/tk-provider-load.json +++ b/mobile/rpc-foundation/goldens/tk-provider-load.json @@ -3,7 +3,7 @@ "family": "tasks.provider-load", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "07a15e900363faa25bc364f5bc1c330f96798abfeaed35210428c2ac558586b8", diff --git a/mobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.json b/mobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.json index f0e434ddcf2..41b96600cee 100644 --- a/mobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.json +++ b/mobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.json @@ -3,7 +3,7 @@ "family": "transport.capability-probe", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.json b/mobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.json index e2ed9f98cd7..e17b7da25d7 100644 --- a/mobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.json +++ b/mobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.json @@ -3,7 +3,7 @@ "family": "transport.capability-probe", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-capability-probe-publishes.json b/mobile/rpc-foundation/goldens/transport-capability-probe-publishes.json index 109e2a79641..ccb11d7defa 100644 --- a/mobile/rpc-foundation/goldens/transport-capability-probe-publishes.json +++ b/mobile/rpc-foundation/goldens/transport-capability-probe-publishes.json @@ -3,7 +3,7 @@ "family": "transport.capability-probe", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.json b/mobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.json index 3997f6cd60f..9973098d83b 100644 --- a/mobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.json +++ b/mobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.json @@ -3,7 +3,7 @@ "family": "transport.capability-probe", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.json b/mobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.json index 5fe7219d16d..f6228aa6d39 100644 --- a/mobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.json +++ b/mobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.json @@ -3,7 +3,7 @@ "family": "transport.host-status-gates", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-host-status-gates-ready.json b/mobile/rpc-foundation/goldens/transport-host-status-gates-ready.json index 4705d6824b1..1098546f302 100644 --- a/mobile/rpc-foundation/goldens/transport-host-status-gates-ready.json +++ b/mobile/rpc-foundation/goldens/transport-host-status-gates-ready.json @@ -3,7 +3,7 @@ "family": "transport.host-status-gates", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.json b/mobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.json index 4ad64f32483..930dc2b649e 100644 --- a/mobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.json +++ b/mobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.json @@ -3,7 +3,7 @@ "family": "transport.host-status-gates", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-pairing-race-both-refused.json b/mobile/rpc-foundation/goldens/transport-pairing-race-both-refused.json index 011c7da798e..f82d2e20f3e 100644 --- a/mobile/rpc-foundation/goldens/transport-pairing-race-both-refused.json +++ b/mobile/rpc-foundation/goldens/transport-pairing-race-both-refused.json @@ -3,7 +3,7 @@ "family": "transport.pairing-race", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-pairing-race-direct-completes-first.json b/mobile/rpc-foundation/goldens/transport-pairing-race-direct-completes-first.json index beab4beb7ba..418a0c0fc3a 100644 --- a/mobile/rpc-foundation/goldens/transport-pairing-race-direct-completes-first.json +++ b/mobile/rpc-foundation/goldens/transport-pairing-race-direct-completes-first.json @@ -3,7 +3,7 @@ "family": "transport.pairing-race", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-pairing-race-relay-completes-first.json b/mobile/rpc-foundation/goldens/transport-pairing-race-relay-completes-first.json index 9c08cd39610..569ff29db55 100644 --- a/mobile/rpc-foundation/goldens/transport-pairing-race-relay-completes-first.json +++ b/mobile/rpc-foundation/goldens/transport-pairing-race-relay-completes-first.json @@ -3,7 +3,7 @@ "family": "transport.pairing-race", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.json b/mobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.json index 094f51dcc06..9690faa2958 100644 --- a/mobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.json +++ b/mobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.json @@ -3,7 +3,7 @@ "family": "transport.pairing-race", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "34b382b13fe75e8ef4002325287c95b3c4db62eeaaf3762af7fbaf6f836c2fa1", diff --git a/mobile/rpc-foundation/goldens/tw-capabilities-advertised.json b/mobile/rpc-foundation/goldens/tw-capabilities-advertised.json index a33899c53e6..83567614693 100644 --- a/mobile/rpc-foundation/goldens/tw-capabilities-advertised.json +++ b/mobile/rpc-foundation/goldens/tw-capabilities-advertised.json @@ -3,7 +3,7 @@ "family": "worktree.runtime-capabilities", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.json b/mobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.json index ad78a98b67b..b722a090a3e 100644 --- a/mobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.json +++ b/mobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.json @@ -3,7 +3,7 @@ "family": "worktree.runtime-capabilities", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.json b/mobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.json index e29031997a7..478282271e1 100644 --- a/mobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.json +++ b/mobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.json @@ -3,7 +3,7 @@ "family": "worktree.runtime-capabilities", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-agent-launched.json b/mobile/rpc-foundation/goldens/tw-create-retry-agent-launched.json index 43cf2c17bb1..87bbe7231d7 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-agent-launched.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-agent-launched.json @@ -3,7 +3,7 @@ "family": "worktree.agent-launch-create", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.json b/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.json index be4dbd35608..b86c462a1d6 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.json b/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.json index 1de6757edf1..7d55c225237 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json b/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json index ea7fd5ef817..aed969a315d 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-created.json b/mobile/rpc-foundation/goldens/tw-create-retry-created.json index fae638b1b80..4c34a3bb8b5 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-created.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-created.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-name-collision.json b/mobile/rpc-foundation/goldens/tw-create-retry-name-collision.json index c7495cdcad9..449100e75af 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-name-collision.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-name-collision.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.json b/mobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.json index 6765290d15a..34ba2993608 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-create-retry-warning-kept.json b/mobile/rpc-foundation/goldens/tw-create-retry-warning-kept.json index 3e2e47dc2ab..f1421d9e34d 100644 --- a/mobile/rpc-foundation/goldens/tw-create-retry-warning-kept.json +++ b/mobile/rpc-foundation/goldens/tw-create-retry-warning-kept.json @@ -3,7 +3,7 @@ "family": "worktree.create-retry", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json b/mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json index 524d18d6db3..a947b6f2c01 100644 --- a/mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json +++ b/mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json @@ -3,7 +3,7 @@ "family": "worktree.hosted-base", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json b/mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json index 2aa4754d638..f7f83c67e4e 100644 --- a/mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json +++ b/mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json @@ -3,7 +3,7 @@ "family": "worktree.hosted-base", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-paste-lookup-resolved.json b/mobile/rpc-foundation/goldens/tw-paste-lookup-resolved.json index 0a31bbeb5b0..ae83198ee1d 100644 --- a/mobile/rpc-foundation/goldens/tw-paste-lookup-resolved.json +++ b/mobile/rpc-foundation/goldens/tw-paste-lookup-resolved.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json b/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json index d397f20748b..0af00ef5423 100644 --- a/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json +++ b/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.json b/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.json index 1c88143ff04..a3590aaea5f 100644 --- a/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.json +++ b/mobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.json @@ -3,7 +3,7 @@ "family": "tasks.paste-lookup", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json b/mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json index 3516410a805..d2d74e6fcdd 100644 --- a/mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json +++ b/mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json @@ -3,7 +3,7 @@ "family": "worktree.setup-hook-trust", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json b/mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json index 4cc3e4a49bf..24e83143bc4 100644 --- a/mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json +++ b/mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json @@ -3,7 +3,7 @@ "family": "worktree.setup-hook-trust", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-smart-search-all-providers.json b/mobile/rpc-foundation/goldens/tw-smart-search-all-providers.json index 05aaf02f13b..04b03ae97f8 100644 --- a/mobile/rpc-foundation/goldens/tw-smart-search-all-providers.json +++ b/mobile/rpc-foundation/goldens/tw-smart-search-all-providers.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json b/mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json index 1098d06e723..99094e1049d 100644 --- a/mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json +++ b/mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-smart-search-linear-listed.json b/mobile/rpc-foundation/goldens/tw-smart-search-linear-listed.json index 2c5a3f2c903..d2e7c7b2e6a 100644 --- a/mobile/rpc-foundation/goldens/tw-smart-search-linear-listed.json +++ b/mobile/rpc-foundation/goldens/tw-smart-search-linear-listed.json @@ -3,7 +3,7 @@ "family": "tasks.smart-source-search", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "52a76b7a830b32287bce14abbe1b9d9ac70e71eafe5b4c6801c2eb14a4150125", diff --git a/mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json b/mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json index 2a9e45a155a..4141740d89b 100644 --- a/mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json +++ b/mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json @@ -3,7 +3,7 @@ "family": "settings-best-effort", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "d3b7f33d810e1fa420ac41a628cde9fe4a9e65fd57f89fbca0a40fc7d74951ab", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.json b/mobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.json index c7ad2ad4837..f166b24b29f 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-source", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-source-presets.json b/mobile/rpc-foundation/goldens/tw-workspace-source-presets.json index 7b49600d281..916d9173fe7 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-source-presets.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-source-presets.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-source", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.json b/mobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.json index d01278e3fa7..fc0946adf48 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-sparse", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json b/mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json index 9478be009c0..cd0132408a7 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-sparse", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.json b/mobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.json index 1ffc263966d..5ec47581ea0 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json b/mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json index 94b921632ed..d06a8840782 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.json b/mobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.json index 9df1fda8bf9..b6549b4de43 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh-local", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json b/mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json index 09db37826a6..8fef89f1f38 100644 --- a/mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json +++ b/mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json @@ -3,7 +3,7 @@ "family": "tasks.workspace-ssh", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e567302ac8acffcfd602c9b323ecf8b5b7c0c4692bda1a4c881011a91d98979", diff --git a/mobile/rpc-foundation/goldens/worktree-catalog-snapshot-unreadable.json b/mobile/rpc-foundation/goldens/worktree-catalog-snapshot-unreadable.json index bd56ef6ec3d..e802de2174a 100644 --- a/mobile/rpc-foundation/goldens/worktree-catalog-snapshot-unreadable.json +++ b/mobile/rpc-foundation/goldens/worktree-catalog-snapshot-unreadable.json @@ -3,7 +3,7 @@ "family": "worktree.catalog-snapshot", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/goldens/worktree-catalog-snapshot.json b/mobile/rpc-foundation/goldens/worktree-catalog-snapshot.json index 688aff036e7..e6308ed49ce 100644 --- a/mobile/rpc-foundation/goldens/worktree-catalog-snapshot.json +++ b/mobile/rpc-foundation/goldens/worktree-catalog-snapshot.json @@ -3,7 +3,7 @@ "family": "worktree.catalog-snapshot", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/goldens/worktree-home-catalog.json b/mobile/rpc-foundation/goldens/worktree-home-catalog.json index 3316607aa91..b76556bb76f 100644 --- a/mobile/rpc-foundation/goldens/worktree-home-catalog.json +++ b/mobile/rpc-foundation/goldens/worktree-home-catalog.json @@ -3,7 +3,7 @@ "family": "worktree.home-catalog", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/goldens/worktree-retired-names.json b/mobile/rpc-foundation/goldens/worktree-retired-names.json index 06e8cb8dd9b..24730723968 100644 --- a/mobile/rpc-foundation/goldens/worktree-retired-names.json +++ b/mobile/rpc-foundation/goldens/worktree-retired-names.json @@ -3,7 +3,7 @@ "family": "worktree.retired-names", "namedDeltas": [], "runnerVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "lockfileSha256": "4568633356187895ccda8e2ff83245c37d5621832c69e268733733b6947971e3", "recorderSha256": "1d8cf6624653d15b5b32bd2871d4d93739d4092a06f10dab24a795702422bb31", "adapterSha256": "4e942ddfbaa0ba6bfc2993969276987f6528ac53765d125e99a830e261f93a8e", diff --git a/mobile/rpc-foundation/pilot-scenarios.json b/mobile/rpc-foundation/pilot-scenarios.json index 9e3e384e7d3..c0fa900c8d7 100644 --- a/mobile/rpc-foundation/pilot-scenarios.json +++ b/mobile/rpc-foundation/pilot-scenarios.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "baseline": "5052501588f2878224b6dfef58ac3d9eb8c8a285", + "baseline": "4a86b2dc565dea05491573afdbff17d6217806d5", "scenarios": [ { "id": "b1", From 8e8a9b38ea2b9a2a621bc891304efa072826e7d5 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 16:53:39 -0400 Subject: [PATCH 023/168] perf(relay): stop indexing the column every control renewal writes (#21286) * perf(relay): stop indexing the column every control renewal writes relay_assignment_activity_expiry indexes expires_at on relay_assignment_activity_leases, and expires_at is what every control renewal updates: ~471 calls/s, all of them non-HOT because a changed indexed column forbids HOT. The index has one reader, the 30s expiry sweep, which seq-scans the whole 14.8k-row table in under a millisecond. Drop it, and set fillfactor to 70 so a renewal has room for a second row version on its own page. Measured on postgres:16-alpine over 14.8k rows, WAL bytes per renewal and HOT ratio: index, fillfactor 100 (today) 0% HOT 371 B index, fillfactor 70 0% HOT 246 B no index, fillfactor 100 0.5% HOT 298 B no index, fillfactor 70 100% HOT 80 B Both are needed: the index makes HOT illegal, and the default fillfactor leaves no page space to make it possible. Neither statement can use the catalog pre-check as it stood. DROP INDEX IF EXISTS resolves the name before it locks, so once the index is gone it costs a catalog miss and takes no lock on the table - pinned in the lock-target census as the one exempt statement. ALTER TABLE SET does take a lock, so it gets a new 'reloption' target kind that asks pg_class.reloptions for the name=value pair, keeping the invariant that no lock-taking statement reaches a warm boot unchecked. * fix(relay): pre-check the activity-expiry drop and let it defer on a lock timeout The drop had no catalog pre-check, so it was sent on every boot, and a 55P03 from it was fatal: apply-postgres-schema throws on a lock timeout with no retry. On the migration boot that combination is a crash loop. All 28 directors reach the same DROP INDEX at once, it needs ACCESS EXCLUSIVE on a table written ~475/s with lock_timeout at 1s, and a boot that fails restarts the instance to re-queue the same DDL behind the same writers. Two changes: - A new 'index-by-name' lock target. A DROP INDEX names no table, so the existing index check could not serve it; this one resolves by name through the search_path with relkind = 'i', which is how the DROP itself resolves, and skips when absent. DROP INDEX now counts as lock-taking in the census, so it is covered rather than exempt, and IF EXISTS is required the way it is on DROP CONSTRAINT. - A 'schema-deferrable' marker, read from a statement's leading comment. A 55P03 on a marked statement logs orca_relay_postgres_schema_object_deferred and leaves the statement unapplied instead of failing the boot; the next boot re-sends it. Both activity-lease migrations carry it. Everything else keeps the old contract and still fails loudly. SchemaApplySummary gains a deferred count so a boot that skipped work is distinguishable from one with nothing to do. Verified against a real server: with the index present and the table held in ACCESS EXCLUSIVE by another session, both statements defer, the boot completes, nothing is half-applied, and the next boot finishes the job. A warm boot now sends neither statement at all. --- .../src/database-postgres-timeout.test.ts | 2 +- cloud/apps/relay/src/database.ts | 24 +++- ...y-schema-catalog-precheck-postgres.test.ts | 75 +++++++++- .../src/relay-schema-lock-targets.test.ts | 67 +++++++-- .../src/apply-postgres-schema.test.ts | 128 ++++++++++++++++-- .../src/apply-postgres-schema.ts | 45 +++++- .../src/catalog-object-precheck.ts | 23 +++- cloud/packages/postgres-schema/src/index.ts | 1 + .../src/schema-lock-target.test.ts | 103 ++++++++++++++ .../postgres-schema/src/schema-lock-target.ts | 61 +++++++-- 10 files changed, 486 insertions(+), 43 deletions(-) diff --git a/cloud/apps/relay/src/database-postgres-timeout.test.ts b/cloud/apps/relay/src/database-postgres-timeout.test.ts index aeb730496c9..adc27cc451c 100644 --- a/cloud/apps/relay/src/database-postgres-timeout.test.ts +++ b/cloud/apps/relay/src/database-postgres-timeout.test.ts @@ -134,7 +134,7 @@ describe('PostgreSQL relay deadlines', () => { statements.every( (statement) => statement === POSTGRES_STATEMENT_STATS_MIGRATION.trim() || - /^(?:CREATE|ALTER TABLE)\b/i.test(body(statement)) + /^(?:CREATE|ALTER TABLE|DROP INDEX)\b/i.test(body(statement)) ) ).toBe(true) // The backfill is DML, so it stays on the deadline-bearing serving pool. diff --git a/cloud/apps/relay/src/database.ts b/cloud/apps/relay/src/database.ts index 53f1836296e..344fee47ab2 100644 --- a/cloud/apps/relay/src/database.ts +++ b/cloud/apps/relay/src/database.ts @@ -524,8 +524,9 @@ CREATE TABLE IF NOT EXISTS relay_assignment_activity_leases ( updated_at BIGINT NOT NULL, PRIMARY KEY (user_id, relay_host_id, activity_id) ); -CREATE INDEX IF NOT EXISTS relay_assignment_activity_expiry - ON relay_assignment_activity_leases(expires_at); +-- expires_at is deliberately unindexed: every control renewal writes it (~471/s), so an index on +-- it makes each renewal a non-HOT update that rewrites index entries. Its only reader is the 30s +-- expiry sweep, which seq-scans 14.8k rows / 7MB in a few milliseconds. CREATE TABLE IF NOT EXISTS relay_control_connection_reservations ( reservation_id TEXT PRIMARY KEY, @@ -645,7 +646,24 @@ export const POSTGRES_SCHEMA_MIGRATIONS = [ ADD COLUMN IF NOT EXISTS host_cooldown_ms BIGINT NOT NULL DEFAULT ${REGIONAL_REHOME_DEFAULT_HOST_COOLDOWN_MS}`, `ALTER TABLE relay_control_capabilities ADD COLUMN IF NOT EXISTS idle_regional_rehome BIGINT NOT NULL DEFAULT 0`, - `ALTER TABLE relay_region_rehome_attempts ADD COLUMN IF NOT EXISTS source_generation BIGINT NOT NULL DEFAULT 0` + `ALTER TABLE relay_region_rehome_attempts ADD COLUMN IF NOT EXISTS source_generation BIGINT NOT NULL DEFAULT 0`, + // Dropped, not created: see the comment on relay_assignment_activity_leases. Deferrable because + // this is the one boot where it has to take ACCESS EXCLUSIVE on a table under continuous write, + // and all 28 directors reach it at once; a lock timeout here must not restart the instance, which + // would only re-queue the same DDL behind the same writers. Once it wins, the pre-check answers + // absent and no later boot sends it at all. + `-- schema-deferrable: one boot has to win ACCESS EXCLUSIVE on a table written ~475/s + DROP INDEX IF EXISTS relay_assignment_activity_expiry`, + // The drop is what makes HOT legal; this is what makes it possible. A renewal can only reuse the + // row's own page when that page has room for a second version, and at the default fillfactor of + // 100 a freshly filled page has none - measured at 0.5% HOT with the index gone and the default, + // against 100% at 70. Takes SHARE UPDATE EXCLUSIVE, which blocks vacuum and DDL but no reader or + // writer, and only for the catalog write. Applies to pages as they refill, so the table converges + // over its own renewal cycle rather than at boot. + // Deferrable for the same reason, though SHARE UPDATE EXCLUSIVE blocks only vacuum and DDL: it + // buys nothing until the drop lands, so a boot that deferred the drop should defer this too. + `-- schema-deferrable: buys nothing until the drop above lands + ALTER TABLE relay_assignment_activity_leases SET (fillfactor = 70)` ] // The exact statement list a Postgres boot applies, in order, so the lock-target census can read diff --git a/cloud/apps/relay/src/relay-schema-catalog-precheck-postgres.test.ts b/cloud/apps/relay/src/relay-schema-catalog-precheck-postgres.test.ts index 2ce9ad9dcb7..cdcc7b3d600 100644 --- a/cloud/apps/relay/src/relay-schema-catalog-precheck-postgres.test.ts +++ b/cloud/apps/relay/src/relay-schema-catalog-precheck-postgres.test.ts @@ -1,5 +1,5 @@ import pg from 'pg' -import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest' +import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import { applyPostgresSchema, catalogObjectPresence, @@ -190,6 +190,79 @@ describePostgres('relay boot-time schema against PostgreSQL', () => { } }) + it('defers the activity-lease migrations and still boots while their table is locked', async () => { + // The migration boot, reproduced: the index is there, the table is locked by someone else, and + // all the drop can do is time out. It has to leave the statement for the next boot rather than + // fail, or 28 directors crash-loop through a stall on a table written ~475/s. + const cold = await openRelayDatabase({ databaseUrl: url, dataDir: '' }) + opened.push(cold) + // Put the database back in its pre-migration shape, which is what makes the drop lock-taking. + await pool.query( + `CREATE INDEX relay_assignment_activity_expiry + ON ${schema}.relay_assignment_activity_leases(expires_at)` + ) + await pool.query(`ALTER TABLE ${schema}.relay_assignment_activity_leases RESET (fillfactor)`) + + const warned: string[] = [] + const warn = vi.spyOn(console, 'warn').mockImplementation((line: string) => { + warned.push(line) + }) + const holder = new pg.Client({ connectionString: url }) + await holder.connect() + await holder.query('BEGIN') + await holder.query( + `LOCK TABLE ${schema}.relay_assignment_activity_leases IN ACCESS EXCLUSIVE MODE` + ) + let summary: Awaited> + try { + summary = await applyPostgresSchema( + relayPostgresSchemaStatements(), + (statement) => pool.query(statement), + { catalogQuery: async (sql, params) => (await pool.query(sql, params)).rows } + ) + } finally { + await holder.query('ROLLBACK') + await holder.end() + warn.mockRestore() + } + + // Both statements deferred, and the boot still applied everything else. + expect(summary.deferred).toBe(2) + expect(summary.ran).toBeGreaterThan(0) + const deferred = warned + .map((line) => JSON.parse(line) as { event?: string; name?: string }) + .filter((event) => event.event === 'orca_relay_postgres_schema_object_deferred') + expect(deferred.map((event) => event.name)).toEqual([ + 'relay_assignment_activity_expiry', + 'fillfactor=70' + ]) + // Nothing was applied, so the next boot has the same work to do, not half of it. + const stillThere = await pool.query( + `SELECT 1 FROM pg_indexes WHERE schemaname = $1 AND indexname = $2`, + [schema, 'relay_assignment_activity_expiry'] + ) + expect(stillThere.rowCount).toBe(1) + + // And the next boot, with the lock gone, finishes the job. + const retry = await applyPostgresSchema( + relayPostgresSchemaStatements(), + (statement) => pool.query(statement), + { catalogQuery: async (sql, params) => (await pool.query(sql, params)).rows } + ) + expect(retry.deferred).toBe(0) + const gone = await pool.query( + `SELECT 1 FROM pg_indexes WHERE schemaname = $1 AND indexname = $2`, + [schema, 'relay_assignment_activity_expiry'] + ) + expect(gone.rowCount).toBe(0) + const options = await pool.query( + `SELECT reloptions FROM pg_class WHERE oid = to_regclass($1)`, + [`${schema}.relay_assignment_activity_leases`] + ) + expect(options.rows[0]?.reloptions).toEqual(['fillfactor=70']) + await pool.end() + }) + it('fails that same boot with 55P03 when the pre-check is not wired in', async () => { // Keeps the test above from passing vacuously: the lock really does block relay's DDL. const cold = await openRelayDatabase({ databaseUrl: url, dataDir: '' }) diff --git a/cloud/apps/relay/src/relay-schema-lock-targets.test.ts b/cloud/apps/relay/src/relay-schema-lock-targets.test.ts index a490a394378..52ae85ccec0 100644 --- a/cloud/apps/relay/src/relay-schema-lock-targets.test.ts +++ b/cloud/apps/relay/src/relay-schema-lock-targets.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest' import { requireSchemaLockTarget, + schemaDeferrable, schemaLockTarget, sqlWithoutComments, takesRelationLock, @@ -72,12 +73,6 @@ const GOLDEN_LOCK_TAKING: SchemaLockTarget[] = [ name: 'relay_cell_drain_attempt_states_cell', skipWhen: 'present' }, - { - kind: 'index', - table: 'relay_assignment_activity_leases', - name: 'relay_assignment_activity_expiry', - skipWhen: 'present' - }, { kind: 'index', table: 'relay_control_connection_reservations', @@ -116,7 +111,14 @@ const GOLDEN_LOCK_TAKING: SchemaLockTarget[] = [ }, { kind: 'column', table: 'relay_region_rehome_control', name: 'host_cooldown_ms', skipWhen: 'present' }, { kind: 'column', table: 'relay_control_capabilities', name: 'idle_regional_rehome', skipWhen: 'present' }, - { kind: 'column', table: 'relay_region_rehome_attempts', name: 'source_generation', skipWhen: 'present' } + { kind: 'column', table: 'relay_region_rehome_attempts', name: 'source_generation', skipWhen: 'present' }, + { kind: 'index-by-name', name: 'relay_assignment_activity_expiry', skipWhen: 'absent' }, + { + kind: 'reloption', + table: 'relay_assignment_activity_leases', + name: 'fillfactor=70', + skipWhen: 'present' + } ] const INDEX_OR_ADD_COLUMN = /^(?:CREATE\s+(?:UNIQUE\s+)?INDEX|ALTER\s+TABLE\s+[^\s]+\s+ADD\s+COLUMN)/i @@ -149,8 +151,12 @@ describe('relay boot-time lock targets', () => { for (const statement of relayPostgresSchemaStatements()) { const target = schemaLockTarget(statement) if (!target) continue - expect(target.name).toMatch(/^[a-z_][a-z0-9_]*$/) - expect(target.table).toMatch(/^[a-z_][a-z0-9_]*$/) + // A reloption is the one target whose name is a pair rather than an identifier, because + // pg_class stores reloptions as `name=value` text and the value is half the question. + const shape = target.kind === 'reloption' ? /^[a-z_][a-z0-9_]*=[A-Za-z0-9_.]+$/ : /^[a-z_][a-z0-9_]*$/ + expect(target.name).toMatch(shape) + // A DROP INDEX names no table, so there is none to check. + if (target.kind !== 'index-by-name') expect(target.table).toMatch(/^[a-z_][a-z0-9_]*$/) } }) @@ -194,7 +200,48 @@ describe('relay boot-time lock targets', () => { it('leaves every statement classifiable once its leading comments are stripped', () => { for (const statement of relayPostgresSchemaStatements()) { - expect(sqlWithoutComments(statement)).toMatch(/^(?:CREATE|ALTER|DO)\s/i) + expect(sqlWithoutComments(statement)).toMatch(/^(?:CREATE|ALTER|DROP|DO)\s/i) } }) + + it('pre-checks the activity-expiry drop by name, and skips it once the index is gone', () => { + // A DROP INDEX takes ACCESS EXCLUSIVE on the index's table for as long as the index is there, + // so it is in the census like any other lock-taking statement. Its target resolves by name + // alone, because the statement names no table and needs none. + const drops = relayPostgresSchemaStatements().filter((statement) => + /^DROP\s/i.test(sqlWithoutComments(statement)) + ) + expect(drops.map(sqlWithoutComments)).toEqual([ + 'DROP INDEX IF EXISTS relay_assignment_activity_expiry' + ]) + for (const statement of drops) { + expect(takesRelationLock(statement)).toBe(true) + expect(schemaLockTarget(statement)).toEqual({ + kind: 'index-by-name', + name: 'relay_assignment_activity_expiry', + skipWhen: 'absent' + }) + } + }) + + it('marks both activity-lease migrations deferrable, and nothing else', () => { + // The two statements a lock timeout must not turn into a crash loop, and the only two: every + // other statement still fails the boot loudly, which is what keeps the marker meaningful. + const deferrable = relayPostgresSchemaStatements().filter(schemaDeferrable) + expect(deferrable.map(sqlWithoutComments)).toEqual([ + 'DROP INDEX IF EXISTS relay_assignment_activity_expiry', + 'ALTER TABLE relay_assignment_activity_leases SET (fillfactor = 70)' + ]) + }) + + it('no longer creates an index on the column every control renewal writes', () => { + // The regression this drop exists to prevent: re-adding it would make ~471 renewals/s non-HOT + // again. A CREATE anywhere in the schema naming that index fails here. + const creates = relayPostgresSchemaStatements().filter((statement) => + /relay_assignment_activity_expiry/i.test(sqlWithoutComments(statement)) + ) + expect(creates.map(sqlWithoutComments)).toEqual([ + 'DROP INDEX IF EXISTS relay_assignment_activity_expiry' + ]) + }) }) diff --git a/cloud/packages/postgres-schema/src/apply-postgres-schema.test.ts b/cloud/packages/postgres-schema/src/apply-postgres-schema.test.ts index b576e3a1129..85aaf0f5b76 100644 --- a/cloud/packages/postgres-schema/src/apply-postgres-schema.test.ts +++ b/cloud/packages/postgres-schema/src/apply-postgres-schema.test.ts @@ -84,7 +84,7 @@ describe('applyPostgresSchema classification', () => { wait: async () => undefined }) expect(query).toHaveBeenCalledTimes(3) - expect(summary).toEqual({ ran: 1, skipped: 0 }) + expect(summary).toEqual({ ran: 1, skipped: 0, deferred: 0 }) }) it('treats an already-applied constraint as skipped rather than an error', async () => { @@ -94,7 +94,110 @@ describe('applyPostgresSchema classification', () => { throw postgresError('42710') }) const summary = await applyPostgresSchema(['ALTER TABLE t ADD CONSTRAINT c CHECK (x > 0)'], query) - expect(summary).toEqual({ ran: 0, skipped: 1 }) + expect(summary).toEqual({ ran: 0, skipped: 1, deferred: 0 }) + }) + + it('treats an index another director already dropped as skipped rather than an error', async () => { + // Every director boots at once on a deploy and all of them send the same DROP INDEX IF EXISTS. + // Only one can win; the losers must not fail their boot over a drop that already happened. + const query = vi.fn(async () => { + throw postgresError('42704') + }) + const summary = await applyPostgresSchema(['DROP INDEX IF EXISTS i'], query) + expect(summary).toEqual({ ran: 0, skipped: 1, deferred: 0 }) + expect(query).toHaveBeenCalledTimes(1) + }) + + it('still propagates 42704 from a statement that is not a DROP IF EXISTS', async () => { + // Keeps the case above narrow: an undefined object anywhere else is a real boot failure. + const query = vi.fn(async () => { + throw postgresError('42704') + }) + await expect( + applyPostgresSchema(['ALTER TABLE t ADD COLUMN IF NOT EXISTS c BIGINT'], query) + ).rejects.toThrow(/42704/) + }) + + it('leaves a deferrable statement unapplied on a lock timeout instead of failing the boot', async () => { + // The crash loop this prevents: 28 directors reach the same DROP INDEX at once on a table + // under continuous write, all of them time out, and every one restarts to re-queue the same + // DDL behind the same writers. + const warned: string[] = [] + vi.spyOn(console, 'warn').mockImplementation((line: string) => { + warned.push(line) + }) + const query = vi.fn(async () => { + throw postgresError('55P03') + }) + const summary = await applyPostgresSchema( + ['-- schema-deferrable: reason\nDROP INDEX IF EXISTS i'], + query + ) + expect(summary).toEqual({ ran: 0, skipped: 0, deferred: 1 }) + expect(query).toHaveBeenCalledTimes(1) + const event = JSON.parse(warned[warned.length - 1] ?? '{}') + expect(event.event).toBe('orca_relay_postgres_schema_object_deferred') + expect(event.code).toBe('55P03') + expect(event.name).toBe('i') + }) + + it('runs the statements after a deferral, rather than abandoning the boot at that point', async () => { + // A deferral is not a failure, so nothing behind it may be skipped: the schema still has + // tables to create, and a boot that stopped here would come up against a partial schema. + const sent: string[] = [] + const query = vi.fn(async (statement: string) => { + sent.push(statement) + if (statement.includes('DROP INDEX')) throw postgresError('55P03') + return undefined + }) + vi.spyOn(console, 'warn').mockImplementation(() => undefined) + const summary = await applyPostgresSchema( + ['-- schema-deferrable: reason\nDROP INDEX IF EXISTS i', 'CREATE TABLE IF NOT EXISTS t (id TEXT)'], + query + ) + expect(summary).toEqual({ ran: 1, skipped: 0, deferred: 1 }) + expect(sent).toHaveLength(2) + }) + + it('still fails the boot on a lock timeout for a statement that is not marked deferrable', async () => { + // Keeps the marker meaningful. An unmarked statement retains the old contract: fail once and + // loudly, because retrying parks every writer behind the same queue again. + vi.spyOn(console, 'error').mockImplementation(() => undefined) + const query = vi.fn(async () => { + throw postgresError('55P03') + }) + await expect(applyPostgresSchema(['DROP INDEX IF EXISTS i'], query)).rejects.toMatchObject({ + code: '55P03' + }) + }) + + it('defers only on a lock timeout, not on any other error from a deferrable statement', async () => { + // A deferrable statement is not a statement whose failures stop mattering. A permission error + // is still a boot failure. + const query = vi.fn(async () => { + throw postgresError('42501') + }) + await expect( + applyPostgresSchema(['-- schema-deferrable: reason\nDROP INDEX IF EXISTS i'], query) + ).rejects.toThrow(/42501/) + }) + + it('asks the catalog for a dropped index by name and skips the DROP once it is gone', async () => { + const query = vi.fn(async (_statement: string) => undefined) + const { catalogQuery, asked } = catalogAnswers([]) + const summary = await applyPostgresSchema(['DROP INDEX IF EXISTS i'], query, { catalogQuery }) + // One parameter, the index name: the statement names no table, and the query references no $2. + expect(asked).toEqual([[expect.stringContaining("relkind = 'i'"), 'i']]) + expect(query).not.toHaveBeenCalled() + expect(summary).toEqual({ ran: 0, skipped: 1, deferred: 0 }) + }) + + it('sends the DROP while the index is still there, which is the boot that has to win', async () => { + const query = vi.fn(async (_statement: string) => undefined) + const { catalogQuery } = catalogAnswers([{}]) + const summary = await applyPostgresSchema(['DROP INDEX IF EXISTS i'], query, { catalogQuery }) + expect(query).toHaveBeenCalledTimes(1) + expect(summary).toEqual({ ran: 1, skipped: 0, deferred: 0 }) }) it('propagates an unrelated error without retrying', async () => { @@ -168,7 +271,7 @@ describe('applyPostgresSchema catalog pre-check', () => { expect(asked).toEqual([ [expect.stringContaining('pg_catalog.pg_index'), 'relay_connection_bases', 'relay_bases_active'] ]) - expect(summary).toEqual({ ran: 1, skipped: 1 }) + expect(summary).toEqual({ ran: 1, skipped: 1, deferred: 0 }) }) it('skips an index the catalog reports as invalid rather than rebuilding it', async () => { @@ -206,7 +309,8 @@ describe('applyPostgresSchema catalog pre-check', () => { expect(JSON.parse(logged[logged.length - 1] ?? '{}')).toEqual({ event: 'orca_push_postgres_schema_applied', ran: 1, - skipped: 1 + skipped: 1, + deferred: 0 }) }) @@ -219,7 +323,7 @@ describe('applyPostgresSchema catalog pre-check', () => { [expect.stringContaining('pg_catalog.pg_attribute'), 'relay_control_capabilities', 'idle'] ]) expect(query.mock.calls.map(([sql]) => sql)).toEqual([statement]) - expect(summary).toEqual({ ran: 1, skipped: 0 }) + expect(summary).toEqual({ ran: 1, skipped: 0, deferred: 0 }) }) it('never probes the catalog for a statement that takes no relation lock', async () => { @@ -247,7 +351,7 @@ describe('applyPostgresSchema catalog pre-check', () => { ] ]) expect(query).not.toHaveBeenCalled() - expect(summary).toEqual({ ran: 0, skipped: 1 }) + expect(summary).toEqual({ ran: 0, skipped: 1, deferred: 0 }) }) it('skips a DROP CONSTRAINT IF EXISTS when the constraint is already gone', async () => { @@ -265,7 +369,7 @@ describe('applyPostgresSchema catalog pre-check', () => { { catalogQuery } ) expect(query).not.toHaveBeenCalled() - expect(summary).toEqual({ ran: 0, skipped: 1 }) + expect(summary).toEqual({ ran: 0, skipped: 1, deferred: 0 }) expect(logged).toContainEqual({ event: 'orca_relay_postgres_schema_object_absent', kind: 'constraint', @@ -281,7 +385,7 @@ describe('applyPostgresSchema catalog pre-check', () => { const statement = 'ALTER TABLE t DROP CONSTRAINT IF EXISTS region_check' const summary = await applyPostgresSchema([statement], query, { catalogQuery }) expect(query.mock.calls.map(([sql]) => sql)).toEqual([statement]) - expect(summary).toEqual({ ran: 1, skipped: 0 }) + expect(summary).toEqual({ ran: 1, skipped: 0, deferred: 0 }) }) it('sends an ADD CONSTRAINT the catalog does not name yet', async () => { @@ -290,14 +394,14 @@ describe('applyPostgresSchema catalog pre-check', () => { const statement = 'ALTER TABLE t ADD CONSTRAINT region_valid CHECK (r IN (1))' const summary = await applyPostgresSchema([statement], query, { catalogQuery }) expect(query.mock.calls.map(([sql]) => sql)).toEqual([statement]) - expect(summary).toEqual({ ran: 1, skipped: 0 }) + expect(summary).toEqual({ ran: 1, skipped: 0, deferred: 0 }) }) it('sends every statement when no catalog query is supplied', async () => { const query = vi.fn(async (_statement: string) => undefined) const summary = await applyPostgresSchema([COMMENTED_TABLE, COMMENTED_INDEX], query) expect(query).toHaveBeenCalledTimes(2) - expect(summary).toEqual({ ran: 2, skipped: 0 }) + expect(summary).toEqual({ ran: 2, skipped: 0, deferred: 0 }) }) }) @@ -316,7 +420,7 @@ describe('applyPostgresSchema concurrent creates', () => { }) expect(query).toHaveBeenCalledTimes(1) expect(asked).toHaveLength(2) - expect(summary).toEqual({ ran: 0, skipped: 1 }) + expect(summary).toEqual({ ran: 0, skipped: 1, deferred: 0 }) }) it('still retries when the catalog says the object is not there after all', async () => { @@ -332,7 +436,7 @@ describe('applyPostgresSchema concurrent creates', () => { wait: async () => undefined }) expect(query).toHaveBeenCalledTimes(2) - expect(summary).toEqual({ ran: 1, skipped: 0 }) + expect(summary).toEqual({ ran: 1, skipped: 0, deferred: 0 }) }) it('retries a CREATE TABLE collision without a catalog re-ask, having no target to ask about', async () => { diff --git a/cloud/packages/postgres-schema/src/apply-postgres-schema.ts b/cloud/packages/postgres-schema/src/apply-postgres-schema.ts index 3417d9ccd70..33490ab1147 100644 --- a/cloud/packages/postgres-schema/src/apply-postgres-schema.ts +++ b/cloud/packages/postgres-schema/src/apply-postgres-schema.ts @@ -25,7 +25,7 @@ export type SchemaStartupOptions = { wait?: (delayMs: number) => Promise } -export type SchemaApplySummary = { ran: number; skipped: number } +export type SchemaApplySummary = { ran: number; skipped: number; deferred: number } function retryDelayMs(attempt: number, random: () => number): number { const ceiling = Math.min(RETRY_BASE_DELAY_MS * 2 ** (attempt - 1), RETRY_MAX_DELAY_MS) @@ -39,6 +39,17 @@ function wait(delayMs: number): Promise { const CREATE_TABLE_IF_NOT_EXISTS = /^CREATE\s+TABLE\s+IF\s+NOT\s+EXISTS\b/i const CREATE_INDEX_IF_NOT_EXISTS = /^CREATE\s+(?:UNIQUE\s+)?INDEX\s+IF\s+NOT\s+EXISTS\b/i const ALTER_TABLE_ADD_CONSTRAINT = /^ALTER\s+TABLE\s+\S+\s+ADD\s+CONSTRAINT\b/i +const DROP_INDEX_IF_EXISTS = /^DROP\s+INDEX\s+(?:CONCURRENTLY\s+)?IF\s+EXISTS\b/i + +// Marked in the schema text, beside the SQL it applies to, and read from the raw statement because +// classification strips comments. Says: this boot may leave the statement unapplied rather than +// fail. Only sound for a statement that is idempotent AND that nothing this boot goes on to do +// depends on, because the database is then simply as it was and the next boot re-sends it. +const DEFERRABLE = /^\s*--[^\n]*\bschema-deferrable\b/ + +export function schemaDeferrable(statement: string): boolean { + return DEFERRABLE.test(statement) +} // `IF NOT EXISTS` only checks the name before the catalog inserts, so the loser of a concurrent // CREATE can fail on the catalog unique index (23505) or, when the winner has already committed by @@ -70,6 +81,14 @@ function constraintAlreadyApplied(error: unknown, sql: string): boolean { ) } +// `IF EXISTS` resolves the name, then locks; between those two steps another director's drop can +// commit and the loser raises 42704 instead of the notice it would have got a moment later. Every +// director boots at once on a deploy, so without this the losers fail their boot over a drop that +// already happened. +function dropAlreadyApplied(error: unknown, sql: string): boolean { + return DROP_INDEX_IF_EXISTS.test(sql) && (error as { code?: unknown } | null)?.code === '42704' +} + function retryableSchemaError(error: unknown, sql: string): boolean { const value = (error as { code?: unknown; constraint?: unknown } | null) ?? {} return RETRYABLE_SCHEMA_CODES.has(String(value.code)) || concurrentCreateCollision(value, sql) @@ -90,7 +109,7 @@ async function nothingToDo( JSON.stringify({ event: `${eventPrefix}_object_${target.skipWhen}`, kind: target.kind, - table: target.table, + table: target.kind === 'index-by-name' ? undefined : target.table, name: target.name, indisvalid: presence.indisvalid }) @@ -108,7 +127,7 @@ export async function applyPostgresSchema( const random = options.random ?? Math.random const pause = options.wait ?? wait const deadlineAt = now() + (options.retryDeadlineMs ?? DEFAULT_RETRY_DEADLINE_MS) - const summary: SchemaApplySummary = { ran: 0, skipped: 0 } + const summary: SchemaApplySummary = { ran: 0, skipped: 0, deferred: 0 } for (const statement of statements) { // Throws when an index or column statement's target cannot be read, rather than sending it @@ -126,7 +145,7 @@ export async function applyPostgresSchema( summary.ran += 1 break } catch (error) { - if (constraintAlreadyApplied(error, sql)) { + if (constraintAlreadyApplied(error, sql) || dropAlreadyApplied(error, sql)) { summary.skipped += 1 break } @@ -135,6 +154,24 @@ export async function applyPostgresSchema( // this boot lost the queue. Relation locks are granted in queue order, so each retry parks // every writer behind it again for another timeout. Fail once, loudly. if (code === LOCK_NOT_AVAILABLE && !options.retryLockTimeout) { + // A deferrable statement yields the queue instead of crash-looping the instance. Every + // director boots at once on a migration, so a table under continuous write can hand the + // whole fleet a lock timeout on the one statement that has to win once; failing the boot + // for it restarts the instance, which re-queues the same DDL behind the same writers. + if (schemaDeferrable(statement)) { + console.warn( + JSON.stringify({ + event: `${eventPrefix}_object_deferred`, + code, + kind: target?.kind, + name: target?.name, + statement: sql.split('\n')[0], + detail: 'could not take its lock; left unapplied for the next boot to retry' + }) + ) + summary.deferred += 1 + break + } console.error( JSON.stringify({ event: `${eventPrefix}_lock_timeout`, diff --git a/cloud/packages/postgres-schema/src/catalog-object-precheck.ts b/cloud/packages/postgres-schema/src/catalog-object-precheck.ts index 66bc5848608..6f923cb4cd2 100644 --- a/cloud/packages/postgres-schema/src/catalog-object-precheck.ts +++ b/cloud/packages/postgres-schema/src/catalog-object-precheck.ts @@ -26,10 +26,26 @@ WHERE attrelid = to_regclass($1) AND attname = $2 AND attnum > 0 AND NOT attisdr const CONSTRAINT_PRESENT = `SELECT 1 FROM pg_catalog.pg_constraint WHERE conrelid = to_regclass($1) AND conname = $2` +// By name through the search_path, with no table condition, because a DROP INDEX has no table to +// condition on and does not need one: a name that resolves to no visible index is nothing to drop. +// `relkind = 'i'` keeps a same-named table or view from answering for an index. Partitioned indexes +// are 'I', which this deliberately does not match - relay has none, and dropping one is not a +// boot-time operation. +const INDEX_BY_NAME_PRESENT = `SELECT 1 FROM pg_catalog.pg_class c +WHERE c.relname = $1 AND c.relkind = 'i' AND pg_catalog.pg_table_is_visible(c.oid)` + +// reloptions is a text[] of `name=value` pairs, absent entirely while the option is at its +// default. Comparing the whole pair is what makes a changed value re-run: `@>` on a different +// value answers no, and the statement runs and overwrites it. +const RELOPTION_PRESENT = `SELECT 1 FROM pg_catalog.pg_class +WHERE oid = to_regclass($1) AND reloptions @> ARRAY[$2]` + const PRESENCE_SQL = { index: INDEX_PRESENT, column: COLUMN_PRESENT, - constraint: CONSTRAINT_PRESENT + constraint: CONSTRAINT_PRESENT, + reloption: RELOPTION_PRESENT, + 'index-by-name': INDEX_BY_NAME_PRESENT } as const export type SchemaCatalogPresence = { present: boolean; indisvalid: unknown } @@ -42,7 +58,10 @@ export async function catalogObjectPresence( target: SchemaLockTarget ): Promise { const sql = PRESENCE_SQL[target.kind] - const rows = await query(sql, [target.table, target.name]) + // The name-only lookup binds one parameter; every other shape binds the table first. Passing a + // parameter the SQL never references is a bind error, not a harmless extra. + const params = target.kind === 'index-by-name' ? [target.name] : [target.table, target.name] + const rows = await query(sql, params) const row = rows[0] return row ? { present: true, indisvalid: row.indisvalid } : { present: false, indisvalid: undefined } } diff --git a/cloud/packages/postgres-schema/src/index.ts b/cloud/packages/postgres-schema/src/index.ts index af101ade9b7..577d375afd6 100644 --- a/cloud/packages/postgres-schema/src/index.ts +++ b/cloud/packages/postgres-schema/src/index.ts @@ -1,5 +1,6 @@ export { applyPostgresSchema, + schemaDeferrable, type SchemaApplySummary, type SchemaStartupOptions } from './apply-postgres-schema.js' diff --git a/cloud/packages/postgres-schema/src/schema-lock-target.test.ts b/cloud/packages/postgres-schema/src/schema-lock-target.test.ts index ffd04dd81cd..80d2714bd2c 100644 --- a/cloud/packages/postgres-schema/src/schema-lock-target.test.ts +++ b/cloud/packages/postgres-schema/src/schema-lock-target.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest' +import { schemaDeferrable } from './apply-postgres-schema.js' import { requireSchemaLockTarget, schemaLockTarget, @@ -417,3 +418,105 @@ describe('dollar-quoted bodies', () => { ) }) }) + +describe('schemaLockTarget storage parameters', () => { + it('reads a storage parameter as a name=value target the catalog can be asked about', () => { + expect(schemaLockTarget('ALTER TABLE t SET (fillfactor = 70)')).toEqual({ + kind: 'reloption', + table: 't', + name: 'fillfactor=70', + skipWhen: 'present' + }) + }) + + it('folds the option name but keeps the value as written, the way pg_class stores the pair', () => { + expect(schemaLockTarget('ALTER TABLE t SET (FillFactor=70)')?.name).toBe('fillfactor=70') + }) + + it('makes a changed value a different target, so it re-runs instead of skipping', () => { + // The failure this prevents: matching on the option name alone would read `fillfactor=100` as + // already satisfying `fillfactor = 70` and skip the statement for the life of the database. + const seventy = schemaLockTarget('ALTER TABLE t SET (fillfactor = 70)') + const eighty = schemaLockTarget('ALTER TABLE t SET (fillfactor = 80)') + expect(seventy?.name).not.toBe(eighty?.name) + }) + + it('refuses a multi-option SET rather than skipping on only the first option', () => { + // Same reason a multi-action ALTER TABLE is refused: skipping on one option would silently + // drop the others for good. + expect(() => + requireSchemaLockTarget('ALTER TABLE t SET (fillfactor = 70, autovacuum_enabled = false)') + ).toThrow(/unparsed_schema_lock_target/) + }) + + it('fails the boot on a SET whose shape it cannot read, rather than sending it unchecked', () => { + // A storage parameter takes a relation lock, so no target means the lock is taken on every + // boot. RESET has no value to compare and is not supported. + expect(() => requireSchemaLockTarget('ALTER TABLE t RESET (fillfactor)')).not.toThrow() + expect(() => requireSchemaLockTarget('ALTER TABLE t SET (fillfactor)')).toThrow( + /unparsed_schema_lock_target/ + ) + }) + + it('takes a relation lock, so the census requires it to carry a target', () => { + expect(takesRelationLock('ALTER TABLE t SET (fillfactor = 70)')).toBe(true) + }) +}) + +describe('schemaLockTarget dropped indexes', () => { + it('resolves a dropped index by name, with no table to name', () => { + expect(schemaLockTarget('DROP INDEX IF EXISTS i')).toEqual({ + kind: 'index-by-name', + name: 'i', + skipWhen: 'absent' + }) + }) + + it('reads CONCURRENTLY as a modifier rather than the index name', () => { + expect(schemaLockTarget('DROP INDEX CONCURRENTLY IF EXISTS i')?.name).toBe('i') + }) + + it('folds an unquoted name and keeps a quoted one, the way relname stores it', () => { + expect(schemaLockTarget('DROP INDEX IF EXISTS MyIndex')?.name).toBe('myindex') + expect(schemaLockTarget('DROP INDEX IF EXISTS "MyIndex"')?.name).toBe('MyIndex') + }) + + it('takes a relation lock, because the index is there on the boot that has to drop it', () => { + expect(takesRelationLock('DROP INDEX IF EXISTS i')).toBe(true) + }) + + it('requires IF EXISTS, so a bare DROP fails the boot instead of running unchecked', () => { + // Same contract as DROP CONSTRAINT: a bare DROP on a missing index is an error the server is + // supposed to raise, and a pre-check that skipped it would swallow that. + expect(() => requireSchemaLockTarget('DROP INDEX i')).toThrow(/unparsed_schema_lock_target/) + }) + + it('refuses a multi-index DROP rather than pre-checking only the first name', () => { + // Skipping on one name would leave the other index in place for the life of the database. + expect(() => requireSchemaLockTarget('DROP INDEX IF EXISTS a, b')).toThrow( + /unparsed_schema_lock_target/ + ) + }) + + it('derives the target through a leading deferrable marker', () => { + // The real shape in relay's schema: the marker is a comment, so classification must see past + // it or the statement would reach the server with no pre-check at all. + const statement = '-- schema-deferrable: reason\nDROP INDEX IF EXISTS i' + expect(sqlWithoutComments(statement)).toBe('DROP INDEX IF EXISTS i') + expect(schemaLockTarget(statement)?.name).toBe('i') + }) +}) + +describe('schemaDeferrable', () => { + it('reads the marker only from a leading comment, never from the SQL body', () => { + // A name or a string containing the word must not make a statement deferrable. + expect(schemaDeferrable('-- schema-deferrable: reason\nDROP INDEX IF EXISTS i')).toBe(true) + expect(schemaDeferrable('DROP INDEX IF EXISTS schema_deferrable')).toBe(false) + expect(schemaDeferrable("CREATE TABLE t (c TEXT DEFAULT 'schema-deferrable')")).toBe(false) + }) + + it('treats an unmarked statement as fatal on a lock timeout, which is the default', () => { + expect(schemaDeferrable('DROP INDEX IF EXISTS i')).toBe(false) + expect(schemaDeferrable('ALTER TABLE t SET (fillfactor = 70)')).toBe(false) + }) +}) diff --git a/cloud/packages/postgres-schema/src/schema-lock-target.ts b/cloud/packages/postgres-schema/src/schema-lock-target.ts index b4b42fc3b5a..8a96dc5fb1d 100644 --- a/cloud/packages/postgres-schema/src/schema-lock-target.ts +++ b/cloud/packages/postgres-schema/src/schema-lock-target.ts @@ -2,14 +2,20 @@ // already exists before the statement joins the lock queue. `table` is kept exactly as the // statement wrote it, schema qualification and quoting included, because it is fed to // `to_regclass`; `name` is the bare identifier the catalog stores in `relname`/`attname`. -export type SchemaLockTarget = { - kind: 'index' | 'column' | 'constraint' - table: string - name: string - // The catalog answer that means this statement has nothing left to do. Creating statements skip - // on present; `DROP CONSTRAINT IF EXISTS` is the inverse, because nothing to drop is done. - skipWhen: 'present' | 'absent' -} +export type SchemaLockTarget = + | { + kind: 'index' | 'column' | 'constraint' | 'reloption' + table: string + name: string + // The catalog answer that means this statement has nothing left to do. Creating statements + // skip on present; `DROP CONSTRAINT IF EXISTS` is the inverse, because nothing to drop is + // done. + skipWhen: 'present' | 'absent' + } + // A `DROP INDEX` names no table, and needs none: an index name that resolves to nothing is + // nothing to drop, whatever table it used to belong to. Resolution is by name through the + // search_path, which is how the DROP itself would resolve it. + | { kind: 'index-by-name'; name: string; skipWhen: 'absent' } // Keywords that sit in an identifier position when the optional clause before them is absent. // Without this, `CREATE UNIQUE INDEX CONCURRENTLY ON t(c)` reads CONCURRENTLY as the index name and @@ -119,10 +125,27 @@ const DROP_CONSTRAINT = new RegExp( 'i' ) +// `IF EXISTS` is required for the same reason it is on DROP CONSTRAINT: a bare `DROP INDEX` on a +// missing index is an error the server is supposed to raise. Without a target the statement throws +// at boot instead, which tells the author to write `IF EXISTS`. +const DROP_INDEX = new RegExp(`^DROP\\s+INDEX\\s+(?:CONCURRENTLY\\s+)?IF\\s+EXISTS\\s+${QUALIFIED}\\s*$`, 'i') + +// One option per statement, and a literal value: the catalog stores reloptions as `name=value` +// text, so the pre-check compares the written pair against that array verbatim. A list of options +// is refused by `hasTopLevelComma` before it reaches here, the same as a multi-action ALTER TABLE. +const SET_RELOPTION = new RegExp( + `^ALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?(?:ONLY\\s+)?${QUALIFIED}\\s+` + + `SET\\s+\\(\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*([A-Za-z0-9_.]+)\\s*\\)\\s*$`, + 'i' +) + // Every statement shape that takes a relation lock before Postgres evaluates its existence test. // `CREATE TABLE IF NOT EXISTS` is absent on purpose: it resolves a name against the schema and // takes no lock on an existing table. -const TAKES_RELATION_LOCK = /^(?:CREATE\s+(?:UNIQUE\s+)?INDEX|ALTER\s+TABLE)\b/i +// `DROP INDEX` is here because it takes ACCESS EXCLUSIVE on the index's table whenever the index is +// actually there, which is every boot until the first one wins. That it takes no lock once the +// index is gone is what the pre-check turns into the steady state, not a reason to omit it. +const TAKES_RELATION_LOCK = /^(?:CREATE\s+(?:UNIQUE\s+)?INDEX|DROP\s+INDEX|ALTER\s+TABLE)\b/i export function takesRelationLock(statement: string): boolean { return TAKES_RELATION_LOCK.test(sqlWithoutComments(statement)) @@ -177,7 +200,9 @@ const MUST_PARSE = [ /^CREATE\s+(?:UNIQUE\s+)?INDEX\b/i, /^ALTER\s+TABLE\b[\s\S]*\bADD\s+COLUMN\b/i, /^ALTER\s+TABLE\b[\s\S]*\bADD\s+CONSTRAINT\b/i, - /^ALTER\s+TABLE\b[\s\S]*\bDROP\s+CONSTRAINT\b/i + /^ALTER\s+TABLE\b[\s\S]*\bDROP\s+CONSTRAINT\b/i, + /^ALTER\s+TABLE\b[\s\S]*\bSET\s+\(/i, + /^DROP\s+INDEX\b/i ] // Derived from the statement itself so a renamed index cannot drift away from its pre-check. @@ -209,6 +234,22 @@ export function schemaLockTarget(statement: string): SchemaLockTarget | undefine skipWhen: 'absent' } } + const droppedIndex = DROP_INDEX.exec(sql) + if (droppedIndex?.[1]) { + return { kind: 'index-by-name', name: catalogName(droppedIndex[1]), skipWhen: 'absent' } + } + const option = SET_RELOPTION.exec(sql) + if (option?.[1] && option[2] && option[3]) { + // Option names are always folded, but the value is stored as written, so only the name goes + // through catalogName. `fillfactor=70` and `fillfactor=80` are different targets, which is + // what makes a changed value re-run rather than skip. + return { + kind: 'reloption', + table: option[1], + name: `${catalogName(option[2])}=${option[3]}`, + skipWhen: 'present' + } + } return undefined } From a634bf9b490051ddc7bad40a574a57f2eb19ad81 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:32:20 -0400 Subject: [PATCH 024/168] test(bench): runtime-graph publication probe and optional CDP CPU throttle (#21107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test(bench): count runtime-graph publications from main The build-provided `__orcaBenchmarkInstrumentation` is gone from the tree, so the typing bench could no longer report graph-publication counts at all. The renderer cannot supply them either: `window.api` is frozen by contextBridge, so `runtime.syncWindowGraph` is not wrappable. Count them where they land instead — main's `runtime:syncWindowGraph` invoke handler — behind ORCA_TYPING_BENCH_GRAPH_PROBE=1, and record the result in the bench report. Measured on an 870-worktree fixture: 21 publications over a 50 s metadata-only window versus ~1,205 with recurring OSC title/status traffic. The long-task fields ship unproven: an injected 250 ms renderer busy-wait produced zero entries even though `longtask` is in `supportedEntryTypes`, so their zeros mean "oracle unverified", not "no long task". The self-test knob exists to make that falsifiable, and the file says so; per-publication build time still needs a separate --cpu-profile run. * test(bench): optional CDP CPU throttle around the typing window * test(bench): report the throttle that ran and the long task the self-test caused Two ways the bench could misreport its own conditions. `cpuThrottleRate` was the requested rate, written into every report, but only two of the three scenarios wrapped their typing window in the throttle — a `--cpu-throttle 4` visible-split run claimed a 4x throttle it never applied. Recording the rate per scenario would have made the report honest; it would also have left one scenario silently ignoring the flag, and a fourth scenario would inherit the same omission. So both: every scenario now goes through one `measureTypingWindow` helper, and the value it returns is the rate the throttle actually applied. `writeBenchReport` takes that composite instead of a bare measurement, so a scenario cannot produce a report without saying what it ran under. Unthrottled runs are unchanged — rate 1 still opens no CDP session. `selfTestLongTaskMs` took the *earliest* long task starting before a cutoff captured after the busy-wait. The observer has been live since probe start, so any unrelated long task from fixture setup satisfied it — the field whose whole job is to prove the oracle is live was the easiest one to fake. The busy-wait now reports its own renderer-clock bounds and the matching entry is the one containing their midpoint: main-thread tasks never overlap, so at most one can, and it is the task the busy-wait ran in. That entry is then withheld from `longTasks`, `longestLongTasks`, and `longTasksAroundPublication`, which had been counting the oracle's injected 250 ms as workload. A zero still means "oracle unproven" — it now also means it honestly. * test(bench): stop the graph probe when the typing run throws * test(e2e): drain queued long-task records before the probe disconnects --- .../run-multi-workspace-typing-bench.mjs | 2 + tests/e2e/runtime-graph-publication-probe.ts | 280 ++++++++++++++++++ ...ntime-graph-publication-probe.unit.test.ts | 57 ++++ ...nal-multi-workspace-typing-latency.spec.ts | 141 ++++++--- 4 files changed, 446 insertions(+), 34 deletions(-) create mode 100644 tests/e2e/runtime-graph-publication-probe.ts create mode 100644 tests/e2e/runtime-graph-publication-probe.unit.test.ts diff --git a/config/scripts/run-multi-workspace-typing-bench.mjs b/config/scripts/run-multi-workspace-typing-bench.mjs index d495fed25ec..2aae129c9b2 100644 --- a/config/scripts/run-multi-workspace-typing-bench.mjs +++ b/config/scripts/run-multi-workspace-typing-bench.mjs @@ -40,6 +40,8 @@ const knobByFlag = { '--metadata-status': 'ORCA_TYPING_BENCH_METADATA_STATUS', '--metadata-titles': 'ORCA_TYPING_BENCH_METADATA_TITLES', '--instrumentation': 'ORCA_TYPING_BENCH_INSTRUMENTATION', + '--graph-probe': 'ORCA_TYPING_BENCH_GRAPH_PROBE', + '--cpu-throttle': 'ORCA_TYPING_BENCH_CPU_THROTTLE', '--label': 'ORCA_TYPING_BENCH_LABEL' } diff --git a/tests/e2e/runtime-graph-publication-probe.ts b/tests/e2e/runtime-graph-publication-probe.ts new file mode 100644 index 00000000000..b61ca666a3a --- /dev/null +++ b/tests/e2e/runtime-graph-publication-probe.ts @@ -0,0 +1,280 @@ +/** + * Diagnostic-only probe for renderer runtime-graph publication cost. + * + * `window.api` is frozen by contextBridge, so the renderer cannot wrap + * `runtime.syncWindowGraph`. Instead this counts publications where they land — + * main's `runtime:syncWindowGraph` invoke handler. + * + * `publications` and `mainHandler` are trustworthy. The long-task fields are NOT + * yet: on 2026-09-16 an injected 250 ms renderer busy-wait produced zero entries + * even though `longtask` is in `supportedEntryTypes`, so a zero there means + * "oracle unproven", not "no long task happened". Run with + * ORCA_TYPING_BENCH_GRAPH_PROBE_SELFTEST_MS and require a non-zero + * `selfTestLongTaskMs` before believing any long-task number. That number comes + * from the entry the busy-wait actually ran in (see `partitionSelfTestLongTask`), + * so unrelated work cannot satisfy it, and the same entry is withheld from every + * workload summary so the oracle does not measure itself. + * + * Renderer-side per-publication build time is unavailable here; attribute it + * with a separate --cpu-profile run instead. + * + * Keep this out of acceptance timing runs (gate: ORCA_TYPING_BENCH_GRAPH_PROBE=1). + */ +import type { ElectronApplication, Page } from '@stablyai/playwright-test' + +const GRAPH_CHANNEL = 'runtime:syncWindowGraph' + +export type DurationSummary = { + count: number + totalMs: number + maxMs: number + p50Ms: number + p90Ms: number +} + +export type LongTaskSample = { startEpochMs: number; durationMs: number } + +/** Renderer-clock bounds of the injected busy-wait, same base as long-task entries. */ +export type RendererLongTaskSelfTestWindow = { startEpochMs: number; endEpochMs: number } + +export type RuntimeGraphPublicationProbeSnapshot = { + mainCounterInstalled: boolean + mainCounterReason: string + rendererObserverInstalled: boolean + rendererObserverReason: string + /** Publications counted at main's invoke handler. */ + publications: number + /** Main-side handler duration (excludes the renderer-side graph build). */ + mainHandler: DurationSummary + /** Gaps between consecutive publications, epoch ms. */ + publicationIntervalMs: DurationSummary + /** Workload long tasks; the self-test's own entry is excluded. */ + longTasks: DurationSummary + /** Non-zero only when the self-test's own entry was observed; proves the oracle is live. */ + selfTestLongTaskMs: number + /** Long tasks whose window contains a publication's main-side arrival. */ + longTasksAroundPublication: DurationSummary + longestLongTasks: LongTaskSample[] +} + +type MainProbeGlobals = { + __orcaGraphPublicationMainProbe?: { + stop: () => { count: number; handlerMs: number[]; atEpochMs: number[] } + } +} + +type RendererProbeWindow = Window & { + __orcaGraphPublicationRendererProbe?: { + stop: () => { timeOrigin: number; longTasks: { start: number; duration: number }[] } + } +} + +function summarize(values: number[]): DurationSummary { + if (values.length === 0) { + return { count: 0, totalMs: 0, maxMs: 0, p50Ms: 0, p90Ms: 0 } + } + const sorted = [...values].sort((a, b) => a - b) + const at = (fraction: number): number => + sorted[Math.min(sorted.length - 1, Math.floor(fraction * sorted.length))] ?? 0 + const round = (value: number): number => Number(value.toFixed(1)) + return { + count: sorted.length, + totalMs: round(sorted.reduce((sum, value) => sum + value, 0)), + maxMs: round(sorted.at(-1) ?? 0), + p50Ms: round(at(0.5)), + p90Ms: round(at(0.9)) + } +} + +/** + * Presence precondition for the long-task oracle: burns a known span on the + * renderer thread so a run that reports zero long tasks has proved it could + * have seen one. Returns the busy-wait's own bounds — a cutoff timestamp would + * let any earlier unrelated long task stand in for it. + */ +export async function injectRendererLongTaskSelfTest( + page: Page, + busyMs: number +): Promise { + return page.evaluate((durationMs) => { + const startedAt = performance.now() + const deadline = startedAt + durationMs + while (performance.now() < deadline) { + // Intentional busy wait: setTimeout would not produce a long task. + } + return { + startEpochMs: performance.timeOrigin + startedAt, + endEpochMs: performance.timeOrigin + performance.now() + } + }, busyMs) +} + +/** + * Main-thread tasks never overlap, so at most one long task can contain the + * busy-wait's midpoint and that one is the task the busy-wait ran in. Anything + * else — including a long task that merely started earlier — leaves the oracle + * unproven rather than falsely satisfied. + */ +export function partitionSelfTestLongTask( + longTasks: LongTaskSample[], + selfTest: RendererLongTaskSelfTestWindow | null +): { selfTestLongTaskMs: number; workloadLongTasks: LongTaskSample[] } { + if (!selfTest) { + return { selfTestLongTaskMs: 0, workloadLongTasks: longTasks } + } + const midpoint = (selfTest.startEpochMs + selfTest.endEpochMs) / 2 + const selfTestTask = longTasks.find( + (task) => task.startEpochMs <= midpoint && midpoint <= task.startEpochMs + task.durationMs + ) + if (!selfTestTask) { + return { selfTestLongTaskMs: 0, workloadLongTasks: longTasks } + } + return { + selfTestLongTaskMs: Number(selfTestTask.durationMs.toFixed(1)), + // Identity, not value: duplicate-looking entries must not be dropped too. + workloadLongTasks: longTasks.filter((task) => task !== selfTestTask) + } +} + +export async function startRuntimeGraphPublicationProbe( + electronApp: ElectronApplication, + page: Page +): Promise<{ main: string; renderer: string }> { + const main = await electronApp.evaluate(({ ipcMain }, channel): string => { + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: diagnostic-only read of Electron's private invoke-handler map; every use is guarded by the shape checks below. + const registry = (ipcMain as unknown as { _invokeHandlers?: Map }) + ._invokeHandlers + if (!(registry instanceof Map)) { + return 'no-invoke-handler-registry' + } + const original = registry.get(channel) + if (typeof original !== 'function') { + return `handler-missing typeof=${typeof original}` + } + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Electron stores invoke handlers as callables; arguments are forwarded unchanged and never introspected. + const call = original as (...args: unknown[]) => unknown + const handlerMs: number[] = [] + const atEpochMs: number[] = [] + const publications = { count: 0, handlerMs, atEpochMs } + const wrapped = async (...args: unknown[]): Promise => { + const startedAt = Date.now() + const startedHr = process.hrtime.bigint() + publications.count += 1 + publications.atEpochMs.push(startedAt) + try { + return await call(...args) + } finally { + publications.handlerMs.push(Number(process.hrtime.bigint() - startedHr) / 1e6) + } + } + registry.set(channel, wrapped) + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: main-process bag read back only by the paired stop() call in this same run. + const globals = globalThis as unknown as MainProbeGlobals + globals.__orcaGraphPublicationMainProbe = { + stop: () => { + if (registry.get(channel) === wrapped) { + registry.set(channel, original) + } + delete globals.__orcaGraphPublicationMainProbe + return publications + } + } + return 'installed' + }, GRAPH_CHANNEL) + + const renderer = await page.evaluate((): string => { + const probeWindow: RendererProbeWindow = window + if (probeWindow.__orcaGraphPublicationRendererProbe) { + return 'already-installed' + } + const supported = PerformanceObserver.supportedEntryTypes ?? [] + if (!supported.includes('longtask')) { + return `longtask-unsupported supported=${supported.join('|')}` + } + const longTasks: { start: number; duration: number }[] = [] + let observer: PerformanceObserver + try { + observer = new PerformanceObserver((list) => { + for (const entry of list.getEntries()) { + longTasks.push({ start: entry.startTime, duration: entry.duration }) + } + }) + observer.observe({ entryTypes: ['longtask'] }) + } catch (error) { + return `longtask-observer-unavailable ${String(error)}` + } + probeWindow.__orcaGraphPublicationRendererProbe = { + stop: () => { + for (const entry of observer.takeRecords()) { + longTasks.push({ start: entry.startTime, duration: entry.duration }) + } + observer.disconnect() + delete probeWindow.__orcaGraphPublicationRendererProbe + return { timeOrigin: performance.timeOrigin, longTasks } + } + } + return 'installed' + }) + + return { main, renderer } +} + +export async function stopRuntimeGraphPublicationProbe( + electronApp: ElectronApplication, + page: Page, + start: { main: string; renderer: string }, + selfTest: RendererLongTaskSelfTestWindow | null = null +): Promise { + const mainResult = + start.main === 'installed' + ? await electronApp.evaluate(() => { + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: reads back the bag installed by startRuntimeGraphPublicationProbe in this run. + const globals = globalThis as unknown as MainProbeGlobals + return globals.__orcaGraphPublicationMainProbe?.stop() ?? null + }) + : null + const rendererResult = + start.renderer === 'installed' + ? await page.evaluate(() => { + const probeWindow: RendererProbeWindow = window + return probeWindow.__orcaGraphPublicationRendererProbe?.stop() ?? null + }) + : null + + const publicationEpochMs = mainResult?.atEpochMs ?? [] + const intervals = publicationEpochMs + .slice(1) + .map((value, index) => value - (publicationEpochMs[index] ?? value)) + const timeOrigin = rendererResult?.timeOrigin ?? 0 + const longTasks: LongTaskSample[] = (rendererResult?.longTasks ?? []).map((task) => ({ + startEpochMs: timeOrigin + task.start, + durationMs: task.duration + })) + const { selfTestLongTaskMs, workloadLongTasks } = partitionSelfTestLongTask(longTasks, selfTest) + // A renderer graph build ends at the invoke; allow slack for IPC transit either way. + const around = workloadLongTasks.filter((task) => + publicationEpochMs.some( + (at) => at >= task.startEpochMs - 5 && at <= task.startEpochMs + task.durationMs + 50 + ) + ) + + return { + mainCounterInstalled: start.main === 'installed', + mainCounterReason: start.main, + rendererObserverInstalled: start.renderer === 'installed', + rendererObserverReason: start.renderer, + publications: mainResult?.count ?? 0, + mainHandler: summarize(mainResult?.handlerMs ?? []), + publicationIntervalMs: summarize(intervals), + longTasks: summarize(workloadLongTasks.map((task) => task.durationMs)), + selfTestLongTaskMs, + longTasksAroundPublication: summarize(around.map((task) => task.durationMs)), + longestLongTasks: [...workloadLongTasks] + .sort((a, b) => b.durationMs - a.durationMs) + .slice(0, 10) + .map((task) => ({ + startEpochMs: task.startEpochMs, + durationMs: Number(task.durationMs.toFixed(1)) + })) + } +} diff --git a/tests/e2e/runtime-graph-publication-probe.unit.test.ts b/tests/e2e/runtime-graph-publication-probe.unit.test.ts new file mode 100644 index 00000000000..c3f46fb5a00 --- /dev/null +++ b/tests/e2e/runtime-graph-publication-probe.unit.test.ts @@ -0,0 +1,57 @@ +import { describe, expect, it } from 'vitest' +import { partitionSelfTestLongTask, type LongTaskSample } from './runtime-graph-publication-probe' + +// The observer is live from probe start, so setup work lands in the array before +// the injected busy-wait. Index 0 is the decoy a start-time cutoff would accept. +const setupTask: LongTaskSample = { startEpochMs: 1_000, durationMs: 120 } +const selfTestTask: LongTaskSample = { startEpochMs: 5_000, durationMs: 260 } +const workloadTask: LongTaskSample = { startEpochMs: 9_000, durationMs: 80 } + +const selfTestWindow = { startEpochMs: 5_005, endEpochMs: 5_255 } + +describe('partitionSelfTestLongTask', () => { + it('attributes the entry the busy-wait ran in, not an earlier one', () => { + const result = partitionSelfTestLongTask( + [setupTask, selfTestTask, workloadTask], + selfTestWindow + ) + expect(result.selfTestLongTaskMs).toBe(260) + expect(result.workloadLongTasks).toEqual([setupTask, workloadTask]) + }) + + it('leaves the oracle unproven when only unrelated tasks were observed', () => { + const result = partitionSelfTestLongTask([setupTask, workloadTask], selfTestWindow) + expect(result.selfTestLongTaskMs).toBe(0) + expect(result.workloadLongTasks).toEqual([setupTask, workloadTask]) + }) + + it('leaves the oracle unproven when a task ends before the busy-wait starts', () => { + // Touches the window's lower edge but does not reach its midpoint. + const result = partitionSelfTestLongTask([{ startEpochMs: 4_900, durationMs: 110 }], { + startEpochMs: 5_000, + endEpochMs: 5_250 + }) + expect(result.selfTestLongTaskMs).toBe(0) + }) + + it('keeps every task when no self-test ran', () => { + const result = partitionSelfTestLongTask([setupTask, workloadTask], null) + expect(result.selfTestLongTaskMs).toBe(0) + expect(result.workloadLongTasks).toEqual([setupTask, workloadTask]) + }) + + it('removes only the matched entry when another has identical values', () => { + const twin: LongTaskSample = { ...selfTestTask } + const result = partitionSelfTestLongTask([selfTestTask, twin], selfTestWindow) + expect(result.workloadLongTasks).toHaveLength(1) + expect(result.workloadLongTasks[0]).toBe(twin) + }) + + it('rounds the reported duration to one decimal', () => { + const result = partitionSelfTestLongTask( + [{ startEpochMs: 5_000, durationMs: 251.2649 }], + selfTestWindow + ) + expect(result.selfTestLongTaskMs).toBe(251.3) + }) +}) diff --git a/tests/e2e/terminal-multi-workspace-typing-latency.spec.ts b/tests/e2e/terminal-multi-workspace-typing-latency.spec.ts index 624ddb1077d..61e5d26a20f 100644 --- a/tests/e2e/terminal-multi-workspace-typing-latency.spec.ts +++ b/tests/e2e/terminal-multi-workspace-typing-latency.spec.ts @@ -83,6 +83,13 @@ import { startAccumulatedTitleTraffic, stopAccumulatedTitleTraffic } from './accumulated-workspace-title-fixture' +import { + injectRendererLongTaskSelfTest, + startRuntimeGraphPublicationProbe, + stopRuntimeGraphPublicationProbe, + type RendererLongTaskSelfTestWindow, + type RuntimeGraphPublicationProbeSnapshot +} from './runtime-graph-publication-probe' const BENCH_ENABLED = process.env.ORCA_TYPING_BENCH === '1' @@ -101,6 +108,12 @@ const PTY_METADATA = process.env.ORCA_TYPING_BENCH_PTY_METADATA === '1' const BENCH_LABEL = process.env.ORCA_TYPING_BENCH_LABEL ?? 'dev' // Request optional probes by default; the report records when the build does not install them. const BENCH_INSTRUMENTATION_REQUESTED = process.env.ORCA_TYPING_BENCH_INSTRUMENTATION !== '0' +// Diagnostic only: patching main's invoke handler is observer overhead, so keep it out of acceptance runs. +const GRAPH_PROBE_REQUESTED = process.env.ORCA_TYPING_BENCH_GRAPH_PROBE === '1' +const GRAPH_PROBE_SELF_TEST_MS = readPositiveInt('ORCA_TYPING_BENCH_GRAPH_PROBE_SELFTEST_MS', 0) +// Estimates a slower single core. Applied only around the typing window: throttling setup would +// change what the fixture manages to build, not just how fast the measured window runs. +const CPU_THROTTLE_RATE = readPositiveInt('ORCA_TYPING_BENCH_CPU_THROTTLE', 1) // Load must outlive setup (pane splits, worktree switches) plus the typing // window; generously padded because setup time varies with pane count. @@ -156,10 +169,55 @@ function spawnCpuPressureWorkers(): ChildProcess[] { ) } +/** Rate 1 is a no-op, so an unthrottled run opens no CDP session at all. */ +async function withRendererCpuThrottle( + page: Page, + rate: number, + run: () => Promise +): Promise<{ result: T; appliedRate: number }> { + if (rate <= 1) { + return { result: await run(), appliedRate: 1 } + } + const session = await page.context().newCDPSession(page) + try { + await session.send('Emulation.setCPUThrottlingRate', { rate }) + return { result: await run(), appliedRate: rate } + } finally { + await session.send('Emulation.setCPUThrottlingRate', { rate: 1 }).catch(() => {}) + await session.detach().catch(() => {}) + } +} + +/** Carries the conditions the window ran under, so the report cannot invent them. */ +type TypingWindowMeasurement = { + measurement: PacedTypingMeasurement + appliedCpuThrottleRate: number +} + +/** + * The only way to obtain a measurement writeBenchReport will accept: a scenario + * that skips the throttle cannot then report one. + */ +async function measureTypingWindow( + page: Page, + runId: string, + sidecarPath: string +): Promise { + const { result, appliedRate } = await withRendererCpuThrottle(page, CPU_THROTTLE_RATE, () => + withTypingRendererCpuProfile(page, process.env.ORCA_TYPING_BENCH_CPU_PROFILE, () => + measurePacedTyping(page, runId, sidecarPath, { + keyCount: KEY_COUNT, + keyCadenceMs: KEY_CADENCE_MS + }) + ) + ) + return { measurement: result, appliedCpuThrottleRate: appliedRate } +} + function writeBenchReport( testInfo: TestInfo, scenario: string, - measurement: PacedTypingMeasurement, + measured: TypingWindowMeasurement, scheduler: SchedulerDebugSnapshot | null, mainDelivery: MainDeliveryDebugSnapshot | null, instrumentation?: unknown, @@ -168,8 +226,10 @@ function writeBenchReport( statusIngressValidation?: AccumulatedStatusIngressValidation | null, scaleCensus?: unknown, accumulatedFixture?: unknown, - ptyWorkload?: unknown + ptyWorkload?: unknown, + graphProbe?: RuntimeGraphPublicationProbeSnapshot | null ): void { + const { measurement, appliedCpuThrottleRate } = measured const report = { benchmark: 'multi-workspace-typing-latency', label: BENCH_LABEL, @@ -208,6 +268,9 @@ function writeBenchReport( 100 ), instrumentationRequested: BENCH_INSTRUMENTATION_REQUESTED, + graphProbeRequested: GRAPH_PROBE_REQUESTED, + // What this scenario actually ran under, not what the flag requested. + cpuThrottleRate: appliedCpuThrottleRate, statusTrafficModel: PTY_METADATA ? 'pty-osc-through-runtime-and-ipc-bridge' : 'electron-ipc-burst-through-production-bridge' @@ -221,7 +284,8 @@ function writeBenchReport( statusIngressValidation: statusIngressValidation ?? null, scaleCensus: scaleCensus ?? null, accumulatedFixture: accumulatedFixture ?? null, - ptyWorkload: ptyWorkload ?? null + ptyWorkload: ptyWorkload ?? null, + graphProbe: graphProbe ?? null } mkdirSync(RESULTS_DIR, { recursive: true }) const stamp = report.timestamp.replace(/[:.]/g, '-') @@ -320,19 +384,12 @@ test.describe('Multi-workspace sustained typing latency bench', () => { try { await resetDeliveryDebug(orcaPage) await startTypingProbe(orcaPage, typingPtyId, probePath, runId) - const measurement = await withTypingRendererCpuProfile( - orcaPage, - process.env.ORCA_TYPING_BENCH_CPU_PROFILE, - () => - measurePacedTyping(orcaPage, runId, sidecarPath, { - keyCount: KEY_COUNT, - keyCadenceMs: KEY_CADENCE_MS - }) - ) + const measured = await measureTypingWindow(orcaPage, runId, sidecarPath) + const { measurement } = measured writeBenchReport( testInfo, 'baseline', - measurement, + measured, await readSchedulerDebug(orcaPage), await readMainDeliveryDebug(orcaPage) ) @@ -372,6 +429,8 @@ test.describe('Multi-workspace sustained typing latency bench', () => { let titleWorkload: { registeredTabs: number; registeredPanes: number } | null = null let statusTrafficStarted = false let instrumentationAvailable = false + let graphProbeStart: { main: string; renderer: string } | null = null + let graphProbeSelfTest: RendererLongTaskSelfTestWindow | null = null let statusIngressValidation: AccumulatedStatusIngressValidation | null = null try { await switchToWorktree(orcaPage, loadWorktreeId) @@ -425,6 +484,16 @@ test.describe('Multi-workspace sustained typing latency bench', () => { if (BENCH_INSTRUMENTATION_REQUESTED) { instrumentationAvailable = await startAccumulatedBenchmarkInstrumentation(orcaPage) } + if (GRAPH_PROBE_REQUESTED) { + graphProbeStart = await startRuntimeGraphPublicationProbe(electronApp, orcaPage) + if (GRAPH_PROBE_SELF_TEST_MS > 0) { + graphProbeSelfTest = await injectRendererLongTaskSelfTest( + orcaPage, + GRAPH_PROBE_SELF_TEST_MS + ) + } + console.log(`[multi-workspace-typing] graph probe: ${JSON.stringify(graphProbeStart)}`) + } if (statusTrafficEnabled) { const statusTraffic = await startAccumulatedStatusTraffic( electronApp, @@ -466,15 +535,8 @@ test.describe('Multi-workspace sustained typing latency bench', () => { .toBe(LOAD_PANES) } await startTypingProbe(orcaPage, typingPtyId, probePath, runId) - const measurement = await withTypingRendererCpuProfile( - orcaPage, - process.env.ORCA_TYPING_BENCH_CPU_PROFILE, - () => - measurePacedTyping(orcaPage, runId, sidecarPath, { - keyCount: KEY_COUNT, - keyCadenceMs: KEY_CADENCE_MS - }) - ) + const measured = await measureTypingWindow(orcaPage, runId, sidecarPath) + const { measurement } = measured const statusWorkload = statusTrafficStarted ? await stopAccumulatedStatusTraffic(electronApp, orcaPage) : null @@ -491,10 +553,19 @@ test.describe('Multi-workspace sustained typing latency bench', () => { ? await stopAccumulatedBenchmarkInstrumentation(orcaPage) : { available: false as const, reason: 'disabled' as const, snapshot: null } instrumentationAvailable = false + const graphProbe = graphProbeStart + ? await stopRuntimeGraphPublicationProbe( + electronApp, + orcaPage, + graphProbeStart, + graphProbeSelfTest + ) + : null + graphProbeStart = null writeBenchReport( testInfo, `hidden-load-${LOAD_PANES}x${LOAD_RATE_KBPS}kbps-cpu${CPU_WORKERS}`, - measurement, + measured, await readSchedulerDebug(orcaPage), await readMainDeliveryDebug(orcaPage), instrumentation, @@ -527,7 +598,8 @@ test.describe('Multi-workspace sustained typing latency bench', () => { ).length } }) - } + }, + graphProbe ) const screenDirectory = path.resolve('.tmp', 'typing-reproduction') mkdirSync(screenDirectory, { recursive: true }) @@ -542,6 +614,14 @@ test.describe('Multi-workspace sustained typing latency bench', () => { if (instrumentationAvailable) { await stopAccumulatedBenchmarkInstrumentation(orcaPage).catch(() => undefined) } + if (graphProbeStart) { + await stopRuntimeGraphPublicationProbe( + electronApp, + orcaPage, + graphProbeStart, + graphProbeSelfTest + ).catch(() => undefined) + } if (statusTrafficStarted) { await stopAccumulatedStatusTraffic(electronApp, orcaPage) } @@ -593,19 +673,12 @@ test.describe('Multi-workspace sustained typing latency bench', () => { await resetDeliveryDebug(orcaPage) await startTypingProbe(orcaPage, typingPane.ptyId, probePath, runId) - const measurement = await withTypingRendererCpuProfile( - orcaPage, - process.env.ORCA_TYPING_BENCH_CPU_PROFILE, - () => - measurePacedTyping(orcaPage, runId, sidecarPath, { - keyCount: KEY_COUNT, - keyCadenceMs: KEY_CADENCE_MS - }) - ) + const measured = await measureTypingWindow(orcaPage, runId, sidecarPath) + const { measurement } = measured writeBenchReport( testInfo, `visible-split-${LOAD_RATE_KBPS}kbps-cpu${CPU_WORKERS}`, - measurement, + measured, await readSchedulerDebug(orcaPage), await readMainDeliveryDebug(orcaPage) ) From eabfbaab88f7b85c5f5b4300bd4211a3d6a8518e Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:34:46 -0400 Subject: [PATCH 025/168] refactor(mobile): drop the unreachable dispose-before-ready notifications arm (#21293) * test(mobile): pin the desktop-notification dispose-before-ready contract Drives `subscribeToDesktopNotifications` through the real `RpcClientStreamRegistry` so the disposer's effect on a later `ready` reply is stated rather than implied. Both cases pass against the current module, before any code is removed. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): drop the unreachable dispose-before-ready notifications arm `disposed` is set only on the first line of the disposer, whose next statement detaches the stream listener in every transport, so the `ready` arm can never observe it. Removing the branch changes no behaviour and moves no golden. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): pin cancel fencing in the relay and logical stream layers The notifications comment claims every transport detaches a listener inside its disposer, but only the stream registry was pinned. Adds the same live/cancelled differential pair to the relay stream manager and the logical client, the latter against a physical session with an inert disposer so only the logical guard can fence the late event. Drops a self-comparing assertion to a length check. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): type the notifications registry fake instead of asserting it The changed-code quality gate rejected three `as` casts. The fake client is now declared `RpcClient`, so the compiler checks it really satisfies the port, and the registry's `unknown` send port is narrowed by a reader that throws on a frame without a string id and method rather than asserting one. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- .../mobile-notifications.test.ts | 94 +++++++++++++++++++ .../src/notifications/mobile-notifications.ts | 7 +- .../mobile-relay-rpc-streams.test.ts | 47 +++++++++- .../stable-logical-rpc-client.test.ts | 47 ++++++++++ 4 files changed, 189 insertions(+), 6 deletions(-) diff --git a/mobile/src/notifications/mobile-notifications.test.ts b/mobile/src/notifications/mobile-notifications.test.ts index ba784520f98..8f85da25e25 100644 --- a/mobile/src/notifications/mobile-notifications.test.ts +++ b/mobile/src/notifications/mobile-notifications.test.ts @@ -2,6 +2,9 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { subscribeToDesktopNotifications } from './mobile-notifications' import { dismissHostPushNotification } from './push-socket-dismissal' import { requestNotificationCatchup } from './push-dismissal-reconciliation' +import { RpcClientStreamRegistry } from '../transport/rpc-client-stream-registry' +import type { RpcClient } from '../transport/rpc-client' +import type { RpcResponse } from '../transport/types' vi.mock('./push-socket-dismissal', () => ({ dismissHostPushNotification: vi.fn(async () => {}) @@ -13,6 +16,69 @@ vi.mock('./notification-permissions', () => ({})) type Handler = (data: unknown) => void +type SentFrame = { id: string; method: string; params: unknown } + +/** The registry sends through an `unknown` port, so name the shape the assertions read. */ +function readSentFrame(request: unknown): SentFrame { + if ( + typeof request !== 'object' || + request === null || + !('id' in request) || + typeof request.id !== 'string' || + !('method' in request) || + typeof request.method !== 'string' + ) { + throw new Error('The stream registry sent a frame without a string id and method') + } + return { + id: request.id, + method: request.method, + params: 'params' in request ? request.params : undefined + } +} + +/** The real stream registry, so dispose-before-ready is answered by the transport, not by a fake. */ +function registryClient() { + const sent: SentFrame[] = [] + const requests: { method: string; params: unknown }[] = [] + let id = 0 + const registry = new RpcClientStreamRegistry({ + nextId: () => `rpc-${++id}`, + deviceToken: 'device-token', + getState: () => 'connected', + sendEncrypted: (request) => { + sent.push(readSentFrame(request)) + return true + } + }) + const client: RpcClient = { + sendRequest: async (method, params) => { + requests.push({ method, params }) + return { id: 'reply-1', ok: true, result: {}, _meta: { runtimeId: 'runtime-1' } } + }, + subscribe: (method, params, onData, options) => + registry.subscribe(method, params, onData, options), + updateTerminalSubscriptionViewport: () => {}, + getState: () => 'connected', + getReconnectAttempt: () => 0, + getLastConnectedAt: () => null, + onStateChange: () => () => {}, + notifyForeground: () => {}, + close: () => {} + } + return { registry, sent, requests, client } +} + +function readyReply(id: string, subscriptionId: string): RpcResponse { + return { + id, + ok: true, + streaming: true, + result: { type: 'ready', subscriptionId }, + _meta: { runtimeId: 'runtime-1' } + } +} + function client() { let handler: Handler | undefined return { @@ -56,4 +122,32 @@ describe('subscribeToDesktopNotifications', () => { await Promise.resolve() expect(dismissHostPushNotification).toHaveBeenCalledWith(dismissal, 'host-1') }) + + it('never runs the ready arm when the disposer ran before the reply landed', () => { + const rpc = registryClient() + const stop = subscribeToDesktopNotifications(rpc.client, 'host-1') + const subscribeFrame = rpc.sent[0]! + expect(subscribeFrame.method).toBe('notifications.subscribe') + + stop() + rpc.registry.handleResponse(readyReply(subscribeFrame.id, 'sub-1')) + + expect(requestNotificationCatchup).not.toHaveBeenCalled() + // The subscription id never reaches this module, so nothing closes the host's stream. + expect(rpc.requests).toEqual([]) + expect(rpc.sent).toHaveLength(1) + }) + + it('closes the host stream when the disposer runs after the ready reply', async () => { + const rpc = registryClient() + const stop = subscribeToDesktopNotifications(rpc.client, 'host-1') + rpc.registry.handleResponse(readyReply(rpc.sent[0]!.id, 'sub-1')) + + stop() + await Promise.resolve() + + expect(rpc.requests).toEqual([ + { method: 'notifications.unsubscribe', params: { subscriptionId: 'sub-1' } } + ]) + }) }) diff --git a/mobile/src/notifications/mobile-notifications.ts b/mobile/src/notifications/mobile-notifications.ts index 48f8e86f63e..62ab9db4b05 100644 --- a/mobile/src/notifications/mobile-notifications.ts +++ b/mobile/src/notifications/mobile-notifications.ts @@ -29,13 +29,10 @@ export function subscribeToDesktopNotifications(client: RpcClient, hostId: strin const params = { includeDesktopSuppressed: true } const unsubscribeStream = client.subscribe('notifications.subscribe', params, (data: unknown) => { const event = data as DismissNotificationEvent | SubscribeResult | { type: string } + // No dispose-before-ready arm: every transport detaches this listener inside + // `unsubscribeStream()`, so a callback that runs at all runs before disposal. if (event.type === 'ready') { subscriptionId = (event as SubscribeResult).subscriptionId - if (disposed) { - unsubscribeServer(subscriptionId) - unsubscribeStream() - return - } // A max watermark asks only which delivered pushes are stale; socket history // never becomes a second OS-notification delivery route. void requestNotificationCatchup(client, hostId, () => disposed).catch(() => {}) diff --git a/mobile/src/transport/mobile-relay-rpc-streams.test.ts b/mobile/src/transport/mobile-relay-rpc-streams.test.ts index b0e59c6bf5b..78cde925330 100644 --- a/mobile/src/transport/mobile-relay-rpc-streams.test.ts +++ b/mobile/src/transport/mobile-relay-rpc-streams.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from 'vitest' -import type { RpcFailure } from './types' +import type { RpcFailure, RpcSuccess } from './types' import { MobileRelayRpcStreams } from './mobile-relay-rpc-streams' function rpcFailure(id: string): RpcFailure { @@ -150,3 +150,48 @@ describe('MobileRelayRpcStreams failure parity', () => { expect(listener).toHaveBeenCalledTimes(1) }) }) + +function readyReply(id: string): RpcSuccess { + return { + id, + ok: true, + streaming: true, + result: { type: 'ready', subscriptionId: 'sub-1' }, + _meta: { runtimeId: 'runtime-1' } + } +} + +describe('MobileRelayRpcStreams cancel fencing', () => { + function subscribed() { + const listener = vi.fn() + const streams = new MobileRelayRpcStreams({ + nextId: () => 'stream-1', + sendFrame: vi.fn(() => true), + waitForConnected: async () => {} + }) + const cancel = streams.subscribe( + 'notifications.subscribe', + { includeDesktopSuppressed: true }, + listener + ) + return { listener, streams, cancel } + } + + it('delivers a ready reply to a live subscription', async () => { + const { listener, streams } = subscribed() + await Promise.resolve() + + expect(streams.handleResponse(readyReply('stream-1'))).toBe(true) + expect(listener).toHaveBeenCalledExactlyOnceWith({ type: 'ready', subscriptionId: 'sub-1' }) + }) + + it('drops a ready reply that lands after the caller cancelled', async () => { + const { listener, streams, cancel } = subscribed() + await Promise.resolve() + + cancel() + + expect(streams.handleResponse(readyReply('stream-1'))).toBe(false) + expect(listener).not.toHaveBeenCalled() + }) +}) diff --git a/mobile/src/transport/stable-logical-rpc-client.test.ts b/mobile/src/transport/stable-logical-rpc-client.test.ts index 6a22b3654a2..ca599f32373 100644 --- a/mobile/src/transport/stable-logical-rpc-client.test.ts +++ b/mobile/src/transport/stable-logical-rpc-client.test.ts @@ -456,3 +456,50 @@ describe('stable logical RPC client', () => { expect(client.getGeneration()).toBe(1) }) }) + +describe('stable logical RPC client subscription fencing', () => { + /** A physical session with an inert disposer, so only the logical guard can fence a late event. */ + function leakySession() { + const session = new FakeSession('connected') + const listeners = new Set<(result: unknown) => void>() + session.subscribe.mockImplementation((_method, _params, listener) => { + listeners.add(listener) + return () => {} + }) + return { + session, + emit(value: unknown): void { + for (const listener of listeners) { + listener(value) + } + } + } + } + + it('delivers a stream event to a live subscription', () => { + const physical = leakySession() + const client = createStableLogicalRpcClient(physical.session, 'lan') + const listener = vi.fn() + client.subscribe('notifications.subscribe', { includeDesktopSuppressed: true }, listener) + + physical.emit({ type: 'ready', subscriptionId: 'sub-1' }) + + expect(listener).toHaveBeenCalledExactlyOnceWith({ type: 'ready', subscriptionId: 'sub-1' }) + }) + + it('drops a stream event that lands after the caller unsubscribed', () => { + const physical = leakySession() + const client = createStableLogicalRpcClient(physical.session, 'lan') + const listener = vi.fn() + const unsubscribe = client.subscribe( + 'notifications.subscribe', + { includeDesktopSuppressed: true }, + listener + ) + + unsubscribe() + physical.emit({ type: 'ready', subscriptionId: 'sub-1' }) + + expect(listener).not.toHaveBeenCalled() + }) +}) From 03714183b8676be0c82a36720eda33623e012089 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:53:34 -0400 Subject: [PATCH 026/168] perf(renderer): stop one pane title update from scanning the global sleeping-record inventory (#21292) * test(perf): pin the pane-title global-scan repro at live-capture scale One setRuntimePaneTitle at Jinjing's scale (~870 workspaces, 1,408 terminal tabs, 857 sleeping records, 20 mounted worktrees) reads 19,711 sleeping-agent records: 23 executions of selectSleepingRecordParkExemptTabIds x 857. The two budget cases are it.fails so the before-state lands in history. Refs STA-7552, STA-7551 * perf(renderer): memoize the sleeping-record park exemption on slice identity A pane title update writes runtimePaneTitlesByTabId, but zustand re-runs every mounted subscriber's selector, so each retained worktree walked the whole sleeping-agent inventory to conclude nothing changed for it. useShallow suppressed the re-render, never the scan. selectSleepingRecordParkExemptTabIds now goes through the existing createWorktreeRecordSelector generation cache, keyed on the record-map identity, so the walk happens once per worktree per real inventory change instead of once per store write. The cache moves from components/sidebar to store/ now that terminal-pane shares it, and takes an isEmpty override so a Set-valued selector can use it. 19,711 sleeping-record reads -> 0 for one title update at capture scale. Refs STA-7552, STA-7551 * test(perf): model the full sidebar fanout and count all four axes The first repro mounted 20 retained workspaces (~60 subscribers) and counted record reads only, which under-models the capture. The sidebar worktree list is not virtualised, so all 870 rows mount and each WorktreeCardStatusSlot opens ~6 subscriptions. Mounting the real row component brings the harness to 5,500 zustand listeners, inside the capture's 5,462-7,478. One setRuntimePaneTitle now reports listener invocations, per-module selector executions, React commits, and records scanned. Before/after the memo, only records scanned moves: 19,711 -> 0. Notification work stays O(mounted workspaces) by construction; each visit is now an identity check. Refs STA-7552, STA-7551 * test(perf): count sidebar-row commits inside the row subtree * refactor(store): teach the selector cache Set/Map emptiness instead of an option * test(perf): drop the duplicate mounts and unasserted counters * refactor(terminal-pane): tighten the park-exemption selector's shape and why * refactor(store): keep the emptiness check off the broad object type * test(renderer): count the three instrumented selector modules in the fanout comment --- .../sidebar/worktree-agent-row-selectors.ts | 2 +- .../sidebar/worktree-card-status-inputs.ts | 2 +- ...e-title-update-global-scan-budget.test.tsx | 419 ++++++++++++++++++ .../sleeping-record-park-exemption.test.ts | 38 +- .../sleeping-record-park-exemption.ts | 73 +-- .../use-terminal-tab-cold-parking.ts | 4 +- .../worktree-record-selector-cache.ts | 10 +- 7 files changed, 509 insertions(+), 39 deletions(-) create mode 100644 src/renderer/src/components/terminal-pane/pane-title-update-global-scan-budget.test.tsx rename src/renderer/src/{components/sidebar => store}/worktree-record-selector-cache.ts (85%) diff --git a/src/renderer/src/components/sidebar/worktree-agent-row-selectors.ts b/src/renderer/src/components/sidebar/worktree-agent-row-selectors.ts index 06ba3979ffe..0319e8a83b5 100644 --- a/src/renderer/src/components/sidebar/worktree-agent-row-selectors.ts +++ b/src/renderer/src/components/sidebar/worktree-agent-row-selectors.ts @@ -13,7 +13,7 @@ import { recordLiveEntriesFullRebuild } from './worktree-agent-live-index-patch' import { selectWorktreeAgentOrchestration } from './worktree-agent-orchestration-index' -import { createWorktreeRecordSelector } from './worktree-record-selector-cache' +import { createWorktreeRecordSelector } from '@/store/worktree-record-selector-cache' import type { TerminalLayoutSnapshot } from '../../../../shared/terminal-tab-types' // Why frozen and exported: card hooks return these from their inactive branch, diff --git a/src/renderer/src/components/sidebar/worktree-card-status-inputs.ts b/src/renderer/src/components/sidebar/worktree-card-status-inputs.ts index cd9f5c4f7c5..43971728deb 100644 --- a/src/renderer/src/components/sidebar/worktree-card-status-inputs.ts +++ b/src/renderer/src/components/sidebar/worktree-card-status-inputs.ts @@ -1,6 +1,6 @@ import type { AppState } from '@/store/types' import type { TerminalPaneLayoutNode } from '../../../../shared/terminal-tab-types' -import { createWorktreeRecordSelector } from './worktree-record-selector-cache' +import { createWorktreeRecordSelector } from '@/store/worktree-record-selector-cache' // Why: these selectors return fresh maps whose top-level values preserve // underlying per-tab references, so callers must compare them shallowly. diff --git a/src/renderer/src/components/terminal-pane/pane-title-update-global-scan-budget.test.tsx b/src/renderer/src/components/terminal-pane/pane-title-update-global-scan-budget.test.tsx new file mode 100644 index 00000000000..f511814cb2b --- /dev/null +++ b/src/renderer/src/components/terminal-pane/pane-title-update-global-scan-budget.test.tsx @@ -0,0 +1,419 @@ +// @vitest-environment happy-dom +/** + * Deterministic, count-based reproduction for STA-7552 (under STA-7551). + * + * Zustand notifies every subscriber synchronously on every `set`, and each + * subscriber re-runs its selector. A single pane title update therefore pays + * for every mounted selector that rescans a global collection to conclude + * nothing changed for it. `useShallow` suppresses the *re-render*, never the + * selector body, so it does not help here. + * + * Scale is Jinjing's live 1.4.203-hourly capture: ~870 workspaces, ~1,400 + * terminal tabs, ~857 sleeping-agent records, ~177 agent-status rows, 20 + * mounted panes/cards. + * + * Scale check: this mount opens 5,500 zustand listeners, inside the capture's + * 5,462-7,478. Most come from the sidebar — the worktree list is not + * virtualised, so all 870 rows mount and each opens ~6 subscriptions. + * + * ONE `setRuntimePaneTitle` at that scale, before/after the park-exemption memo: + * + * metric before after + * zustand listeners 5,500 5,500 + * store notifications 1 1 + * listener invocations 5,500 5,500 + * selector runs: worktree activity summary 896 896 + * selector runs: worktree card status inputs 2,688 2,688 + * selector runs: sleeping-record exemption 23 23 + * sidebar rows committed (of 870) 1 1 + * React commits: retained panes 1 1 + * sleeping-agent records read 19,711 0 + * agent-status rows read 0 0 + * workspace tab buckets read 1,394 1,394 + * + * So notification work is O(mounted workspaces) and this fix does not change + * that — one shared store means every subscriber is visited. What changes is + * the cost of each visit: the scan is gone, and 5,499 of the 5,500 invocations + * were already resolving to "nothing changed for me" without re-rendering. + */ +import React, { act } from 'react' +import { createRoot, type Root } from 'react-dom/client' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { useShallow } from 'zustand/react/shallow' +import { useAppStore } from '@/store' +import { selectTabBarAgentProjections } from '@/components/tab-bar/tab-agent-types-by-tab-id' +import { useWorktreeActivityStatus } from '@/components/sidebar/use-worktree-activity-status' +import { WorktreeCardStatusSlot } from '@/components/sidebar/WorktreeCardStatusSlot' +import { TooltipProvider } from '@/components/ui/tooltip' +import { readStoreListenerCount } from '@/store/store-listener-census' +import type { AgentStatusEntry } from '../../../../shared/agent-status-types' +import type { SleepingAgentSessionRecord } from '../../../../shared/agent-session-resume' +import type { TerminalTab } from '../../../../shared/terminal-tab-types' +import { useTerminalTabColdParking } from './use-terminal-tab-cold-parking' +// Why namespace type imports: vi.mock factories are hoisted, and `typeof import()` +// annotations are banned, so the module shapes come from erased type-only imports. +import type * as SleepingRecordParkExemptionModule from './sleeping-record-park-exemption' +import type * as WorktreeAgentActivitySummaryModule from '@/components/sidebar/worktree-agent-activity-summary' +import type * as WorktreeCardStatusInputsModule from '@/components/sidebar/worktree-card-status-inputs' + +Reflect.set(globalThis, 'IS_REACT_ACT_ENVIRONMENT', true) + +const WORKSPACE_COUNT = 870 +const TERMINAL_TAB_COUNT = 1408 +const SLEEPING_RECORD_COUNT = 857 +const AGENT_STATUS_COUNT = 177 +/** "Live or mounted panes: 20–28" in the capture. */ +const MOUNTED_WORKTREE_COUNT = 20 + +const reads = { sleepingRecords: 0, agentStatusRows: 0, workspaceTabBuckets: 0 } + +/** True per-module selector executions. Cached selectors read no records, so the + * scan counters alone cannot tell "never ran" from "ran and short-circuited". */ +const selectorRuns = vi.hoisted(() => ({ + sleepingRecordParkExemption: 0, + worktreeAgentActivitySummary: 0, + worktreeCardStatusInputs: 0 +})) + +vi.mock('./sleeping-record-park-exemption', async (importOriginal) => { + const actual = await importOriginal() + return { + ...actual, + selectSleepingRecordParkExemptTabIds: ( + ...args: Parameters + ) => { + selectorRuns.sleepingRecordParkExemption += 1 + return actual.selectSleepingRecordParkExemptTabIds(...args) + } + } +}) + +vi.mock('@/components/sidebar/worktree-agent-activity-summary', async (importOriginal) => { + const actual = await importOriginal() + return { + ...actual, + selectWorktreeAgentActivitySummary: ( + ...args: Parameters + ) => { + selectorRuns.worktreeAgentActivitySummary += 1 + return actual.selectWorktreeAgentActivitySummary(...args) + } + } +}) + +vi.mock('@/components/sidebar/worktree-card-status-inputs', async (importOriginal) => { + const actual = await importOriginal() + const count = ( + select: (...args: TArgs) => TResult + ): ((...args: TArgs) => TResult) => { + return (...args: TArgs) => { + selectorRuns.worktreeCardStatusInputs += 1 + return select(...args) + } + } + return { + ...actual, + selectRuntimePaneTitlesForWorktree: count(actual.selectRuntimePaneTitlesForWorktree), + selectLivePtyIdsForWorktree: count(actual.selectLivePtyIdsForWorktree), + selectTerminalLayoutRootsForWorktree: count(actual.selectTerminalLayoutRootsForWorktree) + } +}) + +/** Workspaces whose sidebar-row subtree committed. Why a `Profiler` and not a + * counter in the wrapper: `WorktreeCardStatusSlot` subscribes to the store + * itself, so it can commit without re-executing anything above it. */ +const committedSidebarRows = new Set() +/** React commits of the retained-pane hooks, which live in the probe body. */ +const renders = { retainedPanes: 0 } +/** Store notifications; every live listener is visited on each one. */ +let notifications = 0 + +/** Counts every value read, so a `for…in`/`Object.values` walk is visible without touching production code. */ +function countingRecord( + entries: readonly (readonly [string, T])[], + counter: keyof typeof reads +): Record { + const map: Record = {} + for (const [key, value] of entries) { + Object.defineProperty(map, key, { + enumerable: true, + configurable: true, + get: () => { + reads[counter] += 1 + return value + } + }) + } + return map +} + +const worktreeIds = Array.from( + { length: WORKSPACE_COUNT }, + (_, index) => `repo-1::/repo/wt-${index}` +) +const mountedWorktreeIds = worktreeIds.slice(0, MOUNTED_WORKTREE_COUNT) +/** The pane that receives the title update, on the first mounted workspace. */ +const TARGET_WORKTREE_ID = mountedWorktreeIds[0] +const TARGET_TAB_ID = 'tab-0-0' +const TARGET_PANE_ID = 1 + +function buildTabsByWorktree(): Record { + const tabsByWorktree: Record = {} + let remaining = TERMINAL_TAB_COUNT + for (const [index, worktreeId] of worktreeIds.entries()) { + const count = Math.min(remaining, index < MOUNTED_WORKTREE_COUNT ? 4 : 2) + remaining -= count + tabsByWorktree[worktreeId] = Array.from({ length: count }, (_, tabIndex) => ({ + id: `tab-${index}-${tabIndex}`, + ptyId: `${worktreeId}@@pty-${tabIndex}`, + worktreeId, + title: `tab ${tabIndex}`, + customTitle: null, + color: null, + sortOrder: tabIndex, + createdAt: 0 + })) + if (remaining <= 0) { + break + } + } + return tabsByWorktree +} + +const seededTabsByWorktree = buildTabsByWorktree() +const seededTerminalTabs = Object.values(seededTabsByWorktree).flat() +/** Counting view of the workspace inventory: one hit per bucket actually read, + * so "looked up my own workspace" and "walked all 870" are different numbers. */ +const tabsByWorktree = countingRecord(Object.entries(seededTabsByWorktree), 'workspaceTabBuckets') + +function buildSleepingRecords(): Record { + return countingRecord( + Array.from({ length: SLEEPING_RECORD_COUNT }, (_, index) => { + const worktreeId = worktreeIds[index % WORKSPACE_COUNT] + const tabId = seededTabsByWorktree[worktreeId]?.[0]?.id ?? `tab-${index}-0` + const paneKey = `${tabId}:1` + const record: SleepingAgentSessionRecord = { + paneKey, + tabId, + worktreeId, + agent: 'claude', + providerSession: { key: 'session_id', id: `session-${index}` }, + prompt: 'prompt', + state: 'working', + capturedAt: 1, + updatedAt: 1 + } + return [paneKey, record] as const + }), + 'sleepingRecords' + ) +} + +function buildAgentStatuses(): Record { + const now = Date.now() + return countingRecord( + Array.from({ length: AGENT_STATUS_COUNT }, (_, index) => { + const tabId = seededTerminalTabs[index % seededTerminalTabs.length].id + const paneKey = `${tabId}:1` + const entry: AgentStatusEntry = { + paneKey, + state: 'working', + prompt: 'prompt', + updatedAt: now, + stateStartedAt: now, + stateHistory: [], + agentType: 'claude' + } + return [paneKey, entry] as const + }), + 'agentStatusRows' + ) +} + +const originalState = useAppStore.getState() +let container: HTMLDivElement | null = null +let root: Root | null = null + +const EMPTY_ASSIGNMENTS = new Map() +const noop = (): void => {} +function recordSidebarRowCommit(worktreeId: string): void { + committedSidebarRows.add(worktreeId) +} + +/** The real sidebar row. `useWorktreeActivityStatus` opens ~6 store + * subscriptions per row, which is where the capture's thousands of listeners + * come from — the sidebar is not virtualised, so every workspace is mounted. */ +function SidebarRowProbe({ worktreeId }: { worktreeId: string }): React.JSX.Element { + return ( + + + + ) +} + +/** The three consumers STA-7552 names, mounted per retained workspace. */ +function MountedWorkspaceProbe({ worktreeId }: { worktreeId: string }): null { + renders.retainedPanes += 1 + useTerminalTabColdParking({ + worktreeId, + terminalTabs: seededTabsByWorktree[worktreeId] ?? [], + assignments: EMPTY_ASSIGNMENTS, + isWorktreeActive: worktreeId === TARGET_WORKTREE_ID, + activeTerminalTabId: null, + coldParkTerminalPanes: false, + shouldMeasureHiddenWorktree: false, + activityTerminalPortals: [], + activationDeferredMountTabIds: null + }) + useWorktreeActivityStatus(worktreeId) + useAppStore(useShallow(selectTabBarAgentProjections)) + return null +} + +function mountAtCaptureScale(): void { + useAppStore.setState({ + tabsByWorktree, + sleepingAgentSessionsByPaneKey: buildSleepingRecords(), + agentStatusByPaneKey: buildAgentStatuses(), + agentStatusEpoch: 1, + activeWorktreeId: TARGET_WORKTREE_ID, + runtimePaneTitlesByTabId: { [TARGET_TAB_ID]: { [TARGET_PANE_ID]: 'initial title' } } + }) + + container = document.createElement('div') + document.body.appendChild(container) + root = createRoot(container) + act(() => + root?.render( + + {mountedWorktreeIds.map((worktreeId) => ( + + ))} + {worktreeIds.map((worktreeId) => ( + + ))} + + ) + ) +} + +function liveListenerCount(): number { + const count = readStoreListenerCount() + if (count === null) { + throw new Error('store listener census unavailable') + } + return count +} + +function resetCounters(): void { + reads.sleepingRecords = 0 + reads.agentStatusRows = 0 + reads.workspaceTabBuckets = 0 + committedSidebarRows.clear() + renders.retainedPanes = 0 + notifications = 0 + selectorRuns.sleepingRecordParkExemption = 0 + selectorRuns.worktreeAgentActivitySummary = 0 + selectorRuns.worktreeCardStatusInputs = 0 +} + +function applyOnePaneTitleUpdate(title: string): void { + act(() => { + useAppStore.getState().setRuntimePaneTitle(TARGET_TAB_ID, TARGET_PANE_ID, title) + }) +} + +let stopNotificationProbe: (() => void) | null = null + +beforeEach(() => { + resetCounters() +}) + +afterEach(() => { + stopNotificationProbe?.() + stopNotificationProbe = null + if (root) { + act(() => root?.unmount()) + } + root = null + container?.remove() + container = null + useAppStore.setState(originalState, true) +}) + +describe('one pane title update at live-capture scale', () => { + it('still rescans when the sleeping-record inventory itself changes', () => { + mountAtCaptureScale() + reads.sleepingRecords = 0 + + act(() => { + useAppStore.setState({ sleepingAgentSessionsByPaneKey: buildSleepingRecords() }) + }) + + // Why: correctness floor — a real inventory change must still be observed. + expect(reads.sleepingRecords).toBeGreaterThanOrEqual(SLEEPING_RECORD_COUNT) + }) +}) + +describe('one pane title update: fanout at live-capture scale', () => { + it('reports the four counts the ticket asks for', () => { + mountAtCaptureScale() + const listeners = liveListenerCount() + resetCounters() + stopNotificationProbe = useAppStore.subscribe(() => { + notifications += 1 + }) + + applyOnePaneTitleUpdate('next title') + + // The capture saw 5,462–7,478 listeners; this mount must be the same order, + // and zustand visits every one of them on each notification. + expect(listeners).toBeGreaterThan(5_000) + expect(listeners).toBeLessThan(8_000) + expect(notifications).toBe(1) + + // Every mounted subscriber's selector still runs. That is unchanged by this + // fix and is inherent to one shared store: notification work stays + // O(mounted workspaces), ~3,630 instrumented selector executions. + expect(selectorRuns.worktreeAgentActivitySummary).toBeGreaterThanOrEqual(WORKSPACE_COUNT) + expect(selectorRuns.worktreeCardStatusInputs).toBeGreaterThanOrEqual(WORKSPACE_COUNT * 3) + expect(selectorRuns.sleepingRecordParkExemption).toBeGreaterThanOrEqual(MOUNTED_WORKTREE_COUNT) + + // …but every one of those executions is now an identity check. These three + // counters sit on the state maps themselves, so they catch a walk by ANY of + // the 5,500 subscribers, not only the three instrumented modules. + expect(reads.sleepingRecords).toBe(0) + expect(reads.agentStatusRows).toBe(0) + // One bucket lookup per workspace consumer is a keyed read; a full-inventory + // walk would be that many times 870. + expect(reads.workspaceTabBuckets).toBeLessThan(WORKSPACE_COUNT * 2) + + // Only the workspace that owns the changed pane commits — the other 869 + // sidebar rows hold their identities and bail out. + expect([...committedSidebarRows]).toEqual([TARGET_WORKTREE_ID]) + expect(renders.retainedPanes).toBeLessThanOrEqual(1) + }) + + it('stays flat across repeated updates', () => { + mountAtCaptureScale() + resetCounters() + + applyOnePaneTitleUpdate('title a') + applyOnePaneTitleUpdate('title b') + applyOnePaneTitleUpdate('title c') + + // Why repeat: one update could be served by a memo warmed at mount; three + // prove the cost is independent of how many records the profile stores. + expect(reads.sleepingRecords).toBe(0) + expect(reads.agentStatusRows).toBe(0) + expect([...committedSidebarRows]).toEqual([TARGET_WORKTREE_ID]) + }) +}) diff --git a/src/renderer/src/components/terminal-pane/sleeping-record-park-exemption.test.ts b/src/renderer/src/components/terminal-pane/sleeping-record-park-exemption.test.ts index 0d731bc07f0..37fe50db1a1 100644 --- a/src/renderer/src/components/terminal-pane/sleeping-record-park-exemption.test.ts +++ b/src/renderer/src/components/terminal-pane/sleeping-record-park-exemption.test.ts @@ -19,6 +19,12 @@ function sleepingRecord( } } +function stateWith(sleepingAgentSessionsByPaneKey: Record): { + sleepingAgentSessionsByPaneKey: Record +} { + return { sleepingAgentSessionsByPaneKey } +} + describe('selectSleepingRecordParkExemptTabIds', () => { it.each([ [`tab-1:${LEAF_ID}`, 'tab-1'], @@ -26,14 +32,16 @@ describe('selectSleepingRecordParkExemptTabIds', () => { ])('derives the owner from a valid pane key (%s)', (paneKey, tabId) => { const records = { [paneKey]: sleepingRecord({ paneKey }) } - expect([...selectSleepingRecordParkExemptTabIds(records, 'wt-1')]).toEqual([tabId]) + expect([...selectSleepingRecordParkExemptTabIds(stateWith(records), 'wt-1')]).toEqual([tabId]) }) it('prefers the persisted tab id over the pane key owner', () => { const paneKey = `tab-stale:${LEAF_ID}` const records = { [paneKey]: sleepingRecord({ paneKey, tabId: 'tab-current' }) } - expect([...selectSleepingRecordParkExemptTabIds(records, 'wt-1')]).toEqual(['tab-current']) + expect([...selectSleepingRecordParkExemptTabIds(stateWith(records), 'wt-1')]).toEqual([ + 'tab-current' + ]) }) it('does not invent an owner for a delimiter-less pane key', () => { @@ -41,6 +49,30 @@ describe('selectSleepingRecordParkExemptTabIds', () => { 'orphan-pane-key': sleepingRecord({ paneKey: 'orphan-pane-key' }) } - expect([...selectSleepingRecordParkExemptTabIds(records, 'wt-1')]).toEqual([]) + expect([...selectSleepingRecordParkExemptTabIds(stateWith(records), 'wt-1')]).toEqual([]) + }) + + it('rebuilds when the record map changes and reuses the result when it does not', () => { + const paneKey = `tab-1:${LEAF_ID}` + const records = { [paneKey]: sleepingRecord({ paneKey }) } + const state = stateWith(records) + + const first = selectSleepingRecordParkExemptTabIds(state, 'wt-1') + expect(selectSleepingRecordParkExemptTabIds(state, 'wt-1')).toBe(first) + + const nextPaneKey = `tab-2:${LEAF_ID}` + const grown = stateWith({ ...records, [nextPaneKey]: sleepingRecord({ paneKey: nextPaneKey }) }) + + expect([...selectSleepingRecordParkExemptTabIds(grown, 'wt-1')]).toEqual(['tab-1', 'tab-2']) + }) + + // Why: a memo that serves a stale generation after the workspace's records are + // dropped would pin a hidden pane mounted for the rest of the session. + it('drops a worktree exemption once its records leave the map', () => { + const paneKey = `tab-1:${LEAF_ID}` + const populated = stateWith({ [paneKey]: sleepingRecord({ paneKey }) }) + expect([...selectSleepingRecordParkExemptTabIds(populated, 'wt-1')]).toEqual(['tab-1']) + + expect([...selectSleepingRecordParkExemptTabIds(stateWith({}), 'wt-1')]).toEqual([]) }) }) diff --git a/src/renderer/src/components/terminal-pane/sleeping-record-park-exemption.ts b/src/renderer/src/components/terminal-pane/sleeping-record-park-exemption.ts index 5a2ccec0e2a..4859edade2b 100644 --- a/src/renderer/src/components/terminal-pane/sleeping-record-park-exemption.ts +++ b/src/renderer/src/components/terminal-pane/sleeping-record-park-exemption.ts @@ -1,41 +1,54 @@ import type { SleepingAgentSessionRecord } from '../../../../shared/agent-session-resume' import { parseLegacyNumericPaneKey, parsePaneKey } from '../../../../shared/stable-pane-id' import { isPassiveCompletedHibernationEvidence } from '../../lib/sleeping-agent-pane-ownership' +import { createWorktreeRecordSelector } from '@/store/worktree-record-selector-cache' const EMPTY_TAB_IDS: ReadonlySet = new Set() +type SleepingRecordParkExemptionState = { + sleepingAgentSessionsByPaneKey?: Record +} + /** Tab ids whose panes own a sleeping record a mount can actually consume. * Why: a parked pane can never cold-restore, so per-tab parks must exempt * these — but only these: passive-completed records never resume, * and exempting them would pin a hidden pane mounted indefinitely. - * Callers subscribe through `useShallow`, which compares the set structurally, - * so a write for another worktree cannot re-render this one. Iterates in place — - * `Object.values` would allocate every record on every store write. */ -export function selectSleepingRecordParkExemptTabIds( - sleepingAgentSessionsByPaneKey: Record | undefined, - worktreeId: string -): ReadonlySet { - if (!sleepingAgentSessionsByPaneKey) { - return EMPTY_TAB_IDS + * + * Why memoized on the record map's identity (STA-7552): zustand re-runs every + * mounted subscriber's selector on every store write, so an unrelated pane + * title update used to walk the whole inventory once per retained worktree. + * The map changes only when a record is parked or consumed, so that identity + * is the exact gate. + * Iterates in place — `Object.values` would allocate every record per rebuild. */ +export const selectSleepingRecordParkExemptTabIds = createWorktreeRecordSelector< + SleepingRecordParkExemptionState, + ReadonlySet +>({ + readSources: (state) => [state.sleepingAgentSessionsByPaneKey], + empty: EMPTY_TAB_IDS, + build: ({ sleepingAgentSessionsByPaneKey }, worktreeId) => { + if (!sleepingAgentSessionsByPaneKey) { + return EMPTY_TAB_IDS + } + let owned: Set | null = null + for (const paneKey in sleepingAgentSessionsByPaneKey) { + const record = sleepingAgentSessionsByPaneKey[paneKey] + if (!record || record.worktreeId !== worktreeId) { + continue + } + if (isPassiveCompletedHibernationEvidence(record)) { + continue + } + // Why: malformed pane keys must yield no owner instead of a truncated tab id. + const tabId = + record.tabId ?? + parsePaneKey(record.paneKey)?.tabId ?? + parseLegacyNumericPaneKey(record.paneKey)?.tabId + if (tabId) { + owned ??= new Set() + owned.add(tabId) + } + } + return owned ?? EMPTY_TAB_IDS } - let owned: Set | null = null - for (const paneKey in sleepingAgentSessionsByPaneKey) { - const record = sleepingAgentSessionsByPaneKey[paneKey] - if (!record || record.worktreeId !== worktreeId) { - continue - } - if (isPassiveCompletedHibernationEvidence(record)) { - continue - } - // Why: malformed pane keys must yield no owner instead of a truncated tab id. - const tabId = - record.tabId ?? - parsePaneKey(record.paneKey)?.tabId ?? - parseLegacyNumericPaneKey(record.paneKey)?.tabId - if (tabId) { - owned ??= new Set() - owned.add(tabId) - } - } - return owned ?? EMPTY_TAB_IDS -} +}) diff --git a/src/renderer/src/components/terminal-pane/use-terminal-tab-cold-parking.ts b/src/renderer/src/components/terminal-pane/use-terminal-tab-cold-parking.ts index 2376f842c9a..145a26690c7 100644 --- a/src/renderer/src/components/terminal-pane/use-terminal-tab-cold-parking.ts +++ b/src/renderer/src/components/terminal-pane/use-terminal-tab-cold-parking.ts @@ -119,9 +119,7 @@ export function useTerminalTabColdParking(args: { // Why the worktree-scoped set, not the record map: the map is app-global, so // subscribing to it re-rendered this worktree on every other worktree's write. const sleepingRecordOwnedTabIds = useAppStore( - useShallow((state) => - selectSleepingRecordParkExemptTabIds(state.sleepingAgentSessionsByPaneKey, worktreeId) - ) + useShallow((state) => selectSleepingRecordParkExemptTabIds(state, worktreeId)) ) const terminalTabHiddenSinceRef = useRef(new Map()) // Why: view switches hide every tab at once, so the park clock cannot rank them. diff --git a/src/renderer/src/components/sidebar/worktree-record-selector-cache.ts b/src/renderer/src/store/worktree-record-selector-cache.ts similarity index 85% rename from src/renderer/src/components/sidebar/worktree-record-selector-cache.ts rename to src/renderer/src/store/worktree-record-selector-cache.ts index 220d2d074bf..401eb772f91 100644 --- a/src/renderer/src/components/sidebar/worktree-record-selector-cache.ts +++ b/src/renderer/src/store/worktree-record-selector-cache.ts @@ -6,6 +6,14 @@ type WorktreeRecordGeneration = { byWorktreeId: Map } +/** Why not `Object.keys`: a `Set`/`Map` value has none, so the default check + * would collapse every non-empty one onto the shared empty identity. */ +function isEmptyValue(value: TValue): boolean { + return value instanceof Set || value instanceof Map + ? value.size === 0 + : Object.keys(value).length === 0 +} + function sameSources(previous: readonly unknown[], next: readonly unknown[]): boolean { if (previous.length !== next.length) { return false @@ -52,7 +60,7 @@ export function createWorktreeRecordSelector(opti const built = options.build(state, worktreeId) const carried = generation.carried?.get(worktreeId) let value = built - if (Object.keys(built).length === 0) { + if (isEmptyValue(built)) { value = options.empty } else if (carried && shallow(carried, built)) { value = carried From 49274394fc6a589c298518c613dde12e74da61b2 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 18:39:18 -0400 Subject: [PATCH 027/168] refactor(mobile): put the branch-compare leg on the lifecycle owner, with a currency probe (step 5) (#21299) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(mobile): put the branch-compare leg on the lifecycle owner (step 5) The compare kept three hand-rolled guards for one reply, combined in an `isCurrentLoad()` the four exit points each had to remember to call: `branchCompareGenerationRef` (latest-wins), `currentBranchCompareIdentityRef` (the route identity, written in render) and `mountedRef`. The owner replaces the first two. An attempt now `reset()`s and then `load`s, so the newest attempt is the only one holding a live lease, and the reply is published only through `commit(lease, value)`. What retires a compare is named at the call site: this host, this route identity, this workspace. A compare is a refresh, so neither of the owner's other two mechanisms applies here and the `reset()` before each `load` is what says so: nothing it holds is reusable, and no attempt may share its predecessor's reply. Dropping that line makes the second attempt join the first's request and publish a base ref the user already navigated away from. The identity retire moves into the render-phase adjust-on-prop-change block, where the identity ref was written. Leaving it to the next load's scope is not the same thing: that load only starts once the fresh `git.status` returns, and an in-flight compare would publish the old worktree's commits first. `mountedRef` stays. A detached route has no screen to publish to, which is a fact about the view, not about which reply is current. The three decision points that used to write state mid-flight — no base ref, a refused capability, an unreadable reply — are a returned `BranchCompareOutcome` now, so the loader body writes nothing and the screen is written in one place. That also puts this file under the loader-write source fence. No golden moves: the recording suites reproduce byte for byte. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): correct the compare scope comment to the one call that reads it The pilot's wording named two scope consumers; the compare leg has only `load`. What the scope still adds over the render-phase retire is the structural half: a scope the owner has not seen retires on its own. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): give the lifecycle owner's loader a currency probe A loader that spans two round trips had no way to ask whether its scope had moved, so a superseded attempt sent its second request and was only refused at commit. The probe answers exactly the question commit asks and carries nothing to publish with, so the owner's publish fence is unchanged: a loader that stops on it returns null, which the owner already reads as no value. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): keep a superseded branch compare off the wire Restores request-count parity with main for the one path the migration changed: an attempt superseded while it resolved its base ref used to stop before sending git.branchCompare, and under the owner it sent one and was refused at commit. It now stops on the owner's currency probe between the two legs, so the screen is unchanged and so is the request count. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): say what the probe's missing generation actually is Stripping the directive gives TS2339, a member that does not exist, not a privacy error: the probe has no generation to keep private. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): pin that a detached route sends no compare The detach reset() was the only thing retiring an attempt after the route went away, and deleting it left the suite green. This schedule detaches mid base-ref lookup and asserts nothing reaches git.branchCompare; without the reset() it fails with one request sent. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): drop the scope member the identity key already carries statusIdentityKey is `${hostId}\0${worktreeId}`, so listing worktreeId beside it read as a third fence when it fences nothing new. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): split the compare protocol out of the loaders hook The outcome union, the attempt and the screen mapping are the compare leg's own protocol, not the hook's: nothing in them reaches React. Moved verbatim to mobile-branch-compare-outcome.ts with a unit pin for the mapping, which only the hook's schedules covered before. The hook drops from 283 to 230 lines against a 300 limit. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): correct the joiner comment and narrow the compare sender A joiner never receives the probe: its fn is never invoked, it awaits the originating request's promise, and retire() clears inFlight so none can join across a generation bump. The compare attempt takes the operation sender the convention names rather than a whole RpcClient, which it only ever used as that. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- .../mobile-branch-compare-outcome.test.ts | 42 +++ .../mobile-branch-compare-outcome.ts | 84 ++++++ .../use-mobile-source-control-loaders.test.ts | 264 ++++++++++++++++++ .../use-mobile-source-control-loaders.ts | 142 ++++------ .../generation-scoped-lease-compile-fence.ts | 11 + .../generation-scoped-request-owner.ts | 27 +- mobile/src/transport/lifecycle-owner.test.ts | 76 +++++ 7 files changed, 556 insertions(+), 90 deletions(-) create mode 100644 mobile/src/source-control/mobile-branch-compare-outcome.test.ts create mode 100644 mobile/src/source-control/mobile-branch-compare-outcome.ts create mode 100644 mobile/src/source-control/use-mobile-source-control-loaders.test.ts diff --git a/mobile/src/source-control/mobile-branch-compare-outcome.test.ts b/mobile/src/source-control/mobile-branch-compare-outcome.test.ts new file mode 100644 index 00000000000..ff17d81dd97 --- /dev/null +++ b/mobile/src/source-control/mobile-branch-compare-outcome.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, it } from 'vitest' +import { nextBranchCompareState } from './mobile-branch-compare-outcome' +import type { BranchCompareOutcome } from './mobile-branch-compare-outcome' +import type { MobileGitBranchCompareReply } from './git-compare-reply-schema' +import type { MobileBranchCompareState } from './mobile-source-control-screen-state' + +// The screen mapping on its own. Which attempt reaches it is the owner's question, pinned by the +// schedules in use-mobile-source-control-loaders.test.ts; this is only what each ending renders. + +// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the mapping passes the reply through untouched and reads no member of it. +const REPLY = { summary: { baseRef: 'origin/main' } } as MobileGitBranchCompareReply + +const READY: MobileBranchCompareState = { kind: 'ready', result: REPLY } +const UNAVAILABLE: BranchCompareOutcome = { kind: 'unavailable' } +const FAILED: BranchCompareOutcome = { kind: 'failed', message: 'no base' } + +describe('nextBranchCompareState', () => { + it('publishes a ready compare whatever the previous state was', () => { + const outcome: BranchCompareOutcome = { kind: 'ready', result: REPLY } + expect(nextBranchCompareState(outcome, { kind: 'idle' }, false)).toEqual(READY) + expect(nextBranchCompareState(outcome, { kind: 'error', message: 'old' }, true)).toEqual(READY) + }) + + it('keeps a prior ready compare only when the caller asked for it', () => { + expect(nextBranchCompareState(FAILED, READY, true)).toBe(READY) + expect(nextBranchCompareState(UNAVAILABLE, READY, true)).toBe(READY) + expect(nextBranchCompareState(FAILED, READY, false)).toEqual({ + kind: 'error', + message: 'no base' + }) + }) + + it('separates a host without git from an attempt that failed', () => { + expect(nextBranchCompareState(UNAVAILABLE, { kind: 'loading' }, false)).toEqual({ + kind: 'idle' + }) + expect(nextBranchCompareState(FAILED, { kind: 'loading' }, false)).toEqual({ + kind: 'error', + message: 'no base' + }) + }) +}) diff --git a/mobile/src/source-control/mobile-branch-compare-outcome.ts b/mobile/src/source-control/mobile-branch-compare-outcome.ts new file mode 100644 index 00000000000..6ecaeea34aa --- /dev/null +++ b/mobile/src/source-control/mobile-branch-compare-outcome.ts @@ -0,0 +1,84 @@ +import type { RpcOperationSender } from '../transport/rpc-operation-sender' +import type { RequestCurrency } from '../transport/generation-scoped-request-owner' +import { refusedRpcMessageOrFallback } from '../transport/rpc-refusal-message' +import { resolveMobileBranchCompareBaseRef } from './mobile-branch-base-ref' +import { gitBranchCompareRead } from './mobile-git-read-operations' +import { isMobileGitUnavailableReply } from './mobile-git-status' +import type { MobileGitBranchCompareReply } from './git-compare-reply-schema' +import type { MobileBranchCompareState } from './mobile-source-control-screen-state' + +// The compare leg's own protocol and screen mapping: what one attempt against the worktree's base +// can end as, and what each ending leaves on screen. Nothing here reaches React. + +/** + * Every end one compare attempt can reach, its failures included. The attempt returns its outcome + * instead of writing it, so the screen is written in exactly one place: past the owner's `commit`. + */ +export type BranchCompareOutcome = + | { readonly kind: 'ready'; readonly result: MobileGitBranchCompareReply } + | { readonly kind: 'unavailable' } + | { readonly kind: 'failed'; readonly message: string } + +/** + * Total by construction: a throw here would reach a caller that only ever voids this load. Null is + * the superseded answer, which the owner reads as no value at all. + */ +export async function readBranchCompareOutcome( + client: RpcOperationSender, + worktreeId: string, + currency: RequestCurrency +): Promise { + try { + const baseRef = await resolveMobileBranchCompareBaseRef(client, worktreeId) + // Resolving the base ref is itself a round trip, so the scope may have moved while it was out. + // Stopping here is what keeps a superseded attempt's compare off the wire: refusing it at commit + // would be just as safe on screen but would have sent the request. + if (!currency.isCurrent()) { + return null + } + if (!baseRef) { + return { kind: 'failed', message: 'Unable to resolve the base branch for comparison.' } + } + const reply = await gitBranchCompareRead.request(client, { + worktree: `id:${worktreeId}`, + baseRef + }) + // Why the raw refusal: a host that does not offer git to mobile is a capability gap this + // screen degrades on, and no acceptance policy carries the code and message through. + if (isMobileGitUnavailableReply(reply)) { + return { kind: 'unavailable' } + } + try { + return { kind: 'ready', result: gitBranchCompareRead.interpret(reply) } + } catch (error) { + return { + kind: 'failed', + message: refusedRpcMessageOrFallback(error, 'Unable to load committed changes') + } + } + } catch (err) { + return { + kind: 'failed', + message: err instanceof Error ? err.message : 'Unable to load committed changes' + } + } +} + +/** What an outcome leaves on screen, given what this caller wants kept when the attempt fails. */ +export function nextBranchCompareState( + outcome: BranchCompareOutcome, + previous: MobileBranchCompareState, + preserveReadyOnFailure: boolean +): MobileBranchCompareState { + if (outcome.kind === 'ready') { + return { kind: 'ready', result: outcome.result } + } + // Why: wiping a prior ready compare to idle makes Changes say "No Changes" even when commits + // still exist (e.g. after abort-merge refresh). + if (preserveReadyOnFailure && previous.kind === 'ready') { + return previous + } + return outcome.kind === 'unavailable' + ? { kind: 'idle' } + : { kind: 'error', message: outcome.message } +} diff --git a/mobile/src/source-control/use-mobile-source-control-loaders.test.ts b/mobile/src/source-control/use-mobile-source-control-loaders.test.ts new file mode 100644 index 00000000000..32f24de1b7a --- /dev/null +++ b/mobile/src/source-control/use-mobile-source-control-loaders.test.ts @@ -0,0 +1,264 @@ +import { createElement } from 'react' +import { act, create, type ReactTestRenderer } from 'react-test-renderer' +import { afterEach, describe, expect, it, vi } from 'vitest' +import type { View } from 'react-native' +import type { RpcClient } from '../transport/rpc-client' +import type { RpcResponse } from '../transport/types' +import { useMobileSourceControlLoaders } from './use-mobile-source-control-loaders' + +// The screen-state module these loaders share with the panel pulls the icon set in; none of it is +// reachable from a hook that renders nothing. +vi.mock('lucide-react-native', () => ({ + ArrowDown: vi.fn(), + ArrowDownUp: vi.fn(), + ArrowUp: vi.fn(), + Check: vi.fn(), + CloudUpload: vi.fn(), + GitBranch: vi.fn(), + GitPullRequestArrow: vi.fn(), + History: vi.fn(), + RefreshCw: vi.fn() +})) + +/** + * What the branch-compare leg now rests on and nothing else held: the owner's commit verdict is the + * only thing that decides which of two overlapping compares reaches the screen, the render-phase + * `reset()` is the only thing that retires a compare when the route identity moves, and the owner's + * currency probe is the only thing that keeps a superseded attempt off the wire. All three are + * written as explicit settlement orders rather than timers, so each case states its schedule. + */ + +const WORKTREE = 'repo42::/p' +const IDENTITY = `host-1:${WORKTREE}` + +type PendingCall = { method: string; params: unknown; settle: (reply: RpcResponse) => void } + +function success(result: unknown): RpcResponse { + return { id: 'call', ok: true, result, _meta: { runtimeId: 'runtime-1' } } +} + +const STATUS_REPLY = success({ + branch: 'feature', + head: 'head-oid', + entries: [{ path: 'src/app.ts', status: 'modified', area: 'unstaged', added: 4, removed: 1 }], + upstreamStatus: { hasUpstream: true, ahead: 1, behind: 0, upstreamName: 'origin/feature' } +}) + +const REPO_LIST_REPLY = success({ repos: [] }) + +function worktreeReply(baseRef: string): RpcResponse { + return success({ worktree: { baseRef } }) +} + +function compareReply(baseRef: string): RpcResponse { + return success({ + summary: { + baseRef, + baseOid: 'base-oid', + compareRef: 'feature', + headOid: 'head-oid', + mergeBase: 'merge-base', + changedFiles: 1, + status: 'ready' + }, + entries: [{ path: 'src/app.ts', status: 'modified', added: 2, removed: 1 }] + }) +} + +function fakeClient(calls: PendingCall[]): RpcClient { + const sendRequest = (method: string, params?: unknown): Promise => + new Promise((resolve) => { + calls.push({ method, params, settle: resolve }) + }) + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: these loaders reach `sendRequest` and nothing else on the client; the owner's scope holds the instance by identity without calling it. + return { sendRequest } as RpcClient +} + +/** Settles the oldest unanswered call for `method`, which is what orders one attempt against another. */ +async function settleOldest( + calls: PendingCall[], + method: string, + reply: RpcResponse +): Promise { + const index = calls.findIndex((call) => call.method === method) + if (index === -1) { + throw new Error(`The schedule expected a pending ${method}`) + } + const [call] = calls.splice(index, 1) + await act(async () => call.settle(reply)) +} + +/** Settles the compare an attempt sent, named by the base ref that attempt resolved. */ +async function settleCompare(calls: PendingCall[], baseRef: string): Promise { + const index = calls.findIndex( + (call) => + call.method === 'git.branchCompare' && + typeof call.params === 'object' && + call.params !== null && + 'baseRef' in call.params && + call.params.baseRef === baseRef + ) + if (index === -1) { + throw new Error(`The schedule expected a pending compare against ${baseRef}`) + } + const [call] = calls.splice(index, 1) + await act(async () => call.settle(compareReply(baseRef))) +} + +/** Resolves one whole base-ref lookup: the worktree summary and the repo list go out together. */ +async function settleBaseRefLookup(calls: PendingCall[], baseRef: string): Promise { + await settleOldest(calls, 'worktree.show', worktreeReply(baseRef)) + await settleOldest(calls, 'repo.list', REPO_LIST_REPLY) +} + +function pendingCount(calls: PendingCall[], method: string): number { + return calls.filter((call) => call.method === method).length +} + +/** Stable across renders: the mount effect keys on the callbacks it was handed, so a fresh closure + * per render would re-send the status load and hide the schedule these cases are written in. */ +const IGNORE_ACTION_ERROR = (): void => {} + +// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: `setRootRef` compares the node against null and returns; it reads no member of it. +const ROOT_NODE = {} as View + +describe('useMobileSourceControlLoaders branch compare', () => { + let renderer: ReactTestRenderer | null = null + + afterEach(() => { + act(() => renderer?.unmount()) + renderer = null + }) + + type Loaders = ReturnType + + async function mount( + client: RpcClient, + read: { loaders: Loaders | null } + ): Promise<(identityKey: string, worktreeId: string) => Promise> { + function Harness(props: { identityKey: string; worktreeId: string }): null { + read.loaders = useMobileSourceControlLoaders({ + client, + connState: 'connected', + statusIdentityKey: props.identityKey, + worktreeId: props.worktreeId, + setActionError: IGNORE_ACTION_ERROR + }) + return null + } + await act(async () => { + renderer = create(createElement(Harness, { identityKey: IDENTITY, worktreeId: WORKTREE })) + }) + return async (identityKey, worktreeId) => { + await act(async () => { + renderer?.update(createElement(Harness, { identityKey, worktreeId })) + }) + } + } + + it('lets the newest compare publish and refuses the one it superseded', async () => { + const calls: PendingCall[] = [] + const read: { loaders: Loaders | null } = { loaders: null } + await mount(fakeClient(calls), read) + + // First attempt, taken as far as a compare on the wire. + await settleOldest(calls, 'git.status', STATUS_REPLY) + await settleBaseRefLookup(calls, 'origin/dev') + expect(pendingCount(calls, 'git.branchCompare')).toBe(1) + + // Second attempt, started while the first compare is still out. A forced status load is how the + // screen reaches a second compare: the unforced one would join the in-flight status instead. + await act(async () => { + void read.loaders?.loadStatus({ force: true }) + }) + await settleOldest(calls, 'git.status', STATUS_REPLY) + await settleBaseRefLookup(calls, 'origin/main') + // Two physical compares, not one: an attempt never shares its predecessor's reply. + expect(pendingCount(calls, 'git.branchCompare')).toBe(2) + + await settleCompare(calls, 'origin/main') + expect(read.loaders?.branchCompareState).toEqual({ + kind: 'ready', + result: expect.objectContaining({ + summary: expect.objectContaining({ baseRef: 'origin/main' }) + }) + }) + + // The superseded reply settles last and has nowhere to land. + await settleCompare(calls, 'origin/dev') + expect(read.loaders?.branchCompareState).toEqual({ + kind: 'ready', + result: expect.objectContaining({ + summary: expect.objectContaining({ baseRef: 'origin/main' }) + }) + }) + }) + + it('sends no compare for an attempt superseded while it resolved its base ref', async () => { + const calls: PendingCall[] = [] + const read: { loaders: Loaders | null } = { loaders: null } + await mount(fakeClient(calls), read) + + // First attempt, stopped mid base-ref lookup: nothing of it has reached `git.branchCompare` yet. + await settleOldest(calls, 'git.status', STATUS_REPLY) + expect(pendingCount(calls, 'worktree.show')).toBe(1) + + // Second attempt supersedes it while that lookup is still out. + await act(async () => { + void read.loaders?.loadStatus({ force: true }) + }) + await settleOldest(calls, 'git.status', STATUS_REPLY) + + // The superseded attempt resumes with its base ref in hand and stops on the probe. No compare + // has been settled in this case, so what is pending is everything that was ever sent. + await settleBaseRefLookup(calls, 'origin/dev') + expect(pendingCount(calls, 'git.branchCompare')).toBe(0) + + // Exactly one compare on the wire, the live attempt's, which is the request count main sent. + await settleBaseRefLookup(calls, 'origin/main') + expect(pendingCount(calls, 'git.branchCompare')).toBe(1) + + await settleCompare(calls, 'origin/main') + expect(read.loaders?.branchCompareState).toEqual({ + kind: 'ready', + result: expect.objectContaining({ + summary: expect.objectContaining({ baseRef: 'origin/main' }) + }) + }) + }) + + it('sends no compare for an attempt the route detached under', async () => { + const calls: PendingCall[] = [] + const read: { loaders: Loaders | null } = { loaders: null } + await mount(fakeClient(calls), read) + await act(async () => read.loaders?.setRootRef(ROOT_NODE)) + + await settleOldest(calls, 'git.status', STATUS_REPLY) + expect(pendingCount(calls, 'worktree.show')).toBe(1) + + // The route detaches mid base-ref lookup. Dropping the mount latch is not enough on its own: + // only the detach's `reset()` retires the attempt, and the probe is what reads that. + await act(async () => read.loaders?.setRootRef(null)) + + await settleBaseRefLookup(calls, 'origin/dev') + expect(pendingCount(calls, 'git.branchCompare')).toBe(0) + }) + + it('refuses a compare whose route identity moved while it was out', async () => { + const calls: PendingCall[] = [] + const read: { loaders: Loaders | null } = { loaders: null } + const rerender = await mount(fakeClient(calls), read) + + await settleOldest(calls, 'git.status', STATUS_REPLY) + await settleBaseRefLookup(calls, 'origin/dev') + expect(pendingCount(calls, 'git.branchCompare')).toBe(1) + + // The route is reused for another worktree. Its status load is still out, so no compare has + // entered the new scope yet: this is the window the identity check used to own. + await rerender('host-1:repo42::/other', 'repo42::/other') + expect(read.loaders?.branchCompareState).toEqual({ kind: 'idle' }) + + await settleCompare(calls, 'origin/dev') + expect(read.loaders?.branchCompareState).toEqual({ kind: 'idle' }) + }) +}) diff --git a/mobile/src/source-control/use-mobile-source-control-loaders.ts b/mobile/src/source-control/use-mobile-source-control-loaders.ts index 8ab4cbc0e2a..6856bc2a820 100644 --- a/mobile/src/source-control/use-mobile-source-control-loaders.ts +++ b/mobile/src/source-control/use-mobile-source-control-loaders.ts @@ -1,16 +1,22 @@ import { useCallback, useEffect, useRef, useState, type MutableRefObject } from 'react' import { View } from 'react-native' import type { RpcClient } from '../transport/rpc-client' -import { refusedRpcMessageOrFallback } from '../transport/rpc-refusal-message' +import { + GenerationScopedRequestOwner, + type RequestScope +} from '../transport/generation-scoped-request-owner' import type { ConnectionState } from '../transport/types' -import { resolveMobileBranchCompareBaseRef } from './mobile-branch-base-ref' -import { gitBranchCompareRead, gitStatusHostPayloadRead } from './mobile-git-read-operations' +import { + nextBranchCompareState, + readBranchCompareOutcome, + type BranchCompareOutcome +} from './mobile-branch-compare-outcome' +import { gitStatusHostPayloadRead } from './mobile-git-read-operations' import { isMobileGitTransientRefreshError, isMobileGitUnavailableReply, readMobileGitRefusal } from './mobile-git-status' -import type { MobileGitBranchCompareReply } from './git-compare-reply-schema' import { SELECTOR_RETRY_COUNT, SELECTOR_RETRY_DELAY_MS, @@ -30,6 +36,10 @@ type Params = { onStatusLoadSuccess?: () => void } +/** The compare is the whole worktree against its base, so its request carries no further parameters. */ +type BranchCompareParameters = Readonly> +const WHOLE_WORKTREE: BranchCompareParameters = {} + export type MobileSourceControlLoaders = { screenState: ScreenState setScreenState: (next: ScreenState | ((prev: ScreenState) => ScreenState)) => void @@ -52,11 +62,12 @@ export function useMobileSourceControlLoaders(params: Params): MobileSourceContr kind: 'idle' }) const currentStatusIdentityRef = useRef('') - const currentBranchCompareIdentityRef = useRef('') const loadGenerationRef = useRef(0) - const branchCompareGenerationRef = useRef(0) const mountedRef = useRef(true) const statusLoadInFlightRef = useRef(null) + const branchCompare = useRef( + new GenerationScopedRequestOwner() + ).current // Why: the same route can be reused for another worktree/host (identity change); // a kept-on-failure `ready` state would otherwise show the previous worktree's // data until the fresh load resolves. Reset to loading in the render phase (the @@ -64,102 +75,65 @@ export function useMobileSourceControlLoaders(params: Params): MobileSourceContr const lastResetIdentityRef = useRef(statusIdentityKey) if (lastResetIdentityRef.current !== statusIdentityKey) { lastResetIdentityRef.current = statusIdentityKey + // Retire here rather than leaving it to the next load's scope: that load only starts once the + // fresh status returns, and an in-flight compare would publish the old worktree's commits first. + branchCompare.reset() setScreenState({ kind: 'loading' }) setBranchCompareState({ kind: 'idle' }) } currentStatusIdentityRef.current = statusIdentityKey - currentBranchCompareIdentityRef.current = statusIdentityKey - const setRootRef = useCallback((node: View | null): void => { - if (node !== null) { - mountedRef.current = true - return - } - // Why: source-control RPC loads can outlive the route; invalidate pending - // writes when the screen detaches without a passive cleanup-only Effect. - mountedRef.current = false - loadGenerationRef.current += 1 - branchCompareGenerationRef.current += 1 - }, []) + const setRootRef = useCallback( + (node: View | null): void => { + if (node !== null) { + mountedRef.current = true + return + } + // Why: source-control RPC loads can outlive the route; invalidate pending + // writes when the screen detaches without a passive cleanup-only Effect. + mountedRef.current = false + loadGenerationRef.current += 1 + branchCompare.reset() + }, + [branchCompare] + ) const loadBranchCompare = useCallback( async (options?: { preserveReadyOnFailure?: boolean }) => { - const loadKey = statusIdentityKey - const generation = branchCompareGenerationRef.current + 1 - branchCompareGenerationRef.current = generation - const isCurrentLoad = () => - mountedRef.current && - branchCompareGenerationRef.current === generation && - currentBranchCompareIdentityRef.current === loadKey - + // A compare is a refresh, so nothing it holds is reusable and no attempt may share another's + // reply: retiring first is what makes the newest attempt the only one that can still publish. + branchCompare.reset() if (!worktreeId || !client || connState !== 'connected') { - if (isCurrentLoad()) { + if (mountedRef.current) { setBranchCompareState({ kind: 'idle' }) } return false } + // What retires a compare: this host and this route identity, which is `${hostId}\0${worktreeId}` + // and so carries the workspace already. It is in the scope as well as in the render-phase + // retire, so a scope the owner has not seen still retires on its own if a load ever reaches it + // before that block does. + const scope: RequestScope = [client, statusIdentityKey] setBranchCompareState((prev) => (prev.kind === 'ready' ? prev : { kind: 'loading' })) - try { - const baseRef = await resolveMobileBranchCompareBaseRef(client, worktreeId) - if (!isCurrentLoad()) { - return false - } - if (!baseRef) { - // Why: wiping a prior ready compare to idle makes Changes say "No - // Changes" even when commits still exist (e.g. after abort-merge refresh). - setBranchCompareState((prev) => { - if (options?.preserveReadyOnFailure && prev.kind === 'ready') { - return prev - } - return { - kind: 'error', - message: 'Unable to resolve the base branch for comparison.' - } - }) - return false - } - const reply = await gitBranchCompareRead.request(client, { - worktree: `id:${worktreeId}`, - baseRef - }) - if (!isCurrentLoad()) { - return false - } - // Why the raw refusal: a host that does not offer git to mobile is a capability gap this - // screen degrades on, and no acceptance policy carries the code and message through. - if (isMobileGitUnavailableReply(reply)) { - setBranchCompareState((prev) => { - if (options?.preserveReadyOnFailure && prev.kind === 'ready') { - return prev - } - return { kind: 'idle' } - }) - return false - } - let compared: MobileGitBranchCompareReply - try { - compared = gitBranchCompareRead.interpret(reply) - } catch (error) { - throw new Error(refusedRpcMessageOrFallback(error, 'Unable to load committed changes')) - } - setBranchCompareState({ kind: 'ready', result: compared }) - return true - } catch (err) { - if (!isCurrentLoad()) { - return false - } - const message = err instanceof Error ? err.message : 'Unable to load committed changes' - setBranchCompareState((prev) => { - if (options?.preserveReadyOnFailure && prev.kind === 'ready') { - return prev - } - return { kind: 'error', message } - }) + const loaded = await branchCompare.load(scope, WHOLE_WORKTREE, (currency) => + readBranchCompareOutcome(client, worktreeId, currency) + ) + // The mount latch is not the owner's to keep: a detached route has no screen to publish to, + // which is a fact about the view, not about which reply is current. + if (!loaded || !mountedRef.current) { return false } + if (branchCompare.commit(loaded.lease, loaded.value) !== 'committed') { + return false + } + const outcome = loaded.value + setBranchCompareState((prev) => + nextBranchCompareState(outcome, prev, options?.preserveReadyOnFailure === true) + ) + return outcome.kind === 'ready' }, - [client, connState, statusIdentityKey, worktreeId] + [branchCompare, client, connState, statusIdentityKey, worktreeId] ) const loadStatus = useCallback( diff --git a/mobile/src/transport/generation-scoped-lease-compile-fence.ts b/mobile/src/transport/generation-scoped-lease-compile-fence.ts index 039b03f51b8..24e293b73c0 100644 --- a/mobile/src/transport/generation-scoped-lease-compile-fence.ts +++ b/mobile/src/transport/generation-scoped-lease-compile-fence.ts @@ -55,6 +55,17 @@ export function fenceLoaderOnlyReturns(): void { // @ts-expect-error the loader publishes by returning the owner's value, not some other type void paths.load(scope, { query: 'a' }, async () => 'not-a-path-list') void paths.load(scope, { query: 'a' }, async () => null) + void paths.load(scope, { query: 'a' }, async (currency) => (currency.isCurrent() ? ['a'] : null)) +} + +export function fenceCurrencyIsAProbeNotALease(): void { + void paths.load(scope, { query: 'a' }, async (currency) => { + // @ts-expect-error the probe answers currency and is not the lease, so it cannot publish + paths.commit(currency, ['a']) + // @ts-expect-error the probe exposes no generation at all, so there is no member to read + void currency.generation + return null + }) } // @ts-expect-error the lease carries its generation privately; a caller cannot read or compare it diff --git a/mobile/src/transport/generation-scoped-request-owner.ts b/mobile/src/transport/generation-scoped-request-owner.ts index c8655c58a6d..b6b9771fe29 100644 --- a/mobile/src/transport/generation-scoped-request-owner.ts +++ b/mobile/src/transport/generation-scoped-request-owner.ts @@ -37,6 +37,15 @@ export type RequestLease = { readonly [LEASE_VALUE]?: (value: Value) => void } +/** + * Handed to a loader so it can stop before sending a request whose scope has already moved. A probe + * and nothing else: it answers the same question `commit` asks and carries no way to publish, so the + * loader still has only its return value to say anything with. + */ +export type RequestCurrency = { + readonly isCurrent: () => boolean +} + /** Named rather than boolean: a refused publish says which fence refused it. */ type RequestCommitVerdict = 'committed' | 'retired-generation' | 'foreign-owner' @@ -91,12 +100,14 @@ export class GenerationScopedRequestOwner Promise + fn: (currency: RequestCurrency) => Promise ): Promise | null> { const key = this.enter(scope, parameters) return this.inFlight.get(key) ?? this.start(key, fn) @@ -122,16 +133,20 @@ export class GenerationScopedRequestOwner Promise + fn: (currency: RequestCurrency) => Promise ): Promise | null> { - const lease: RequestLease = { - [LEASE_STATE]: { key, generation: this.currentGeneration, owner: this.owner } + const state: RequestLeaseState = { key, generation: this.currentGeneration, owner: this.owner } + const lease: RequestLease = { [LEASE_STATE]: state } + // One probe per physical request. A joiner never sees it: its `fn` is never invoked, it awaits + // this promise, and `retire()` clears `inFlight`, so no joiner can join across a generation bump. + const currency: RequestCurrency = { + isCurrent: () => state.generation === this.currentGeneration } let loaded: Promise try { // Called here rather than off a microtask so the request reaches the wire in the turn the // caller asked for it, which is what orders it against its siblings. - loaded = fn() + loaded = fn(currency) } catch (error) { loaded = Promise.reject(error instanceof Error ? error : new Error(String(error))) } diff --git a/mobile/src/transport/lifecycle-owner.test.ts b/mobile/src/transport/lifecycle-owner.test.ts index b8350a6b714..3a5d8aca82f 100644 --- a/mobile/src/transport/lifecycle-owner.test.ts +++ b/mobile/src/transport/lifecycle-owner.test.ts @@ -6,6 +6,7 @@ import { describe, expect, it } from 'vitest' import { GenerationScopedRequestOwner, type LoadedRequest, + type RequestCurrency, type RequestScope } from './generation-scoped-request-owner' @@ -277,6 +278,81 @@ describe('owner boundaries', () => { }) }) +describe('currency probe', () => { + it('flips under a request still in flight when a reset retires it', async () => { + const owner: Owner = new GenerationScopedRequestOwner() + const scope = scopeAt('w1', 1) + const request = pending() + const probes: RequestCurrency[] = [] + const loaded = owner.load(scope, QUERY, (currency) => { + probes.push(currency) + return request.start() + }) + expect(probes[0]?.isCurrent()).toBe(true) + + owner.reset() + expect(probes[0]?.isCurrent()).toBe(false) + + // The probe answers the question `commit` asks, so a loader that ignored it lands here instead. + request.resolve(['stale.ts']) + const stale = await settled(loaded) + expect(owner.commit(stale.lease, stale.value)).toBe('retired-generation') + }) + + it('flips when the next load enters on a scope the owner has not seen', async () => { + const owner: Owner = new GenerationScopedRequestOwner() + const request = pending() + const probes: RequestCurrency[] = [] + const loaded = owner.load(scopeAt('A', 1), QUERY, (currency) => { + probes.push(currency) + return request.start() + }) + expect(probes[0]?.isCurrent()).toBe(true) + + void owner.load(scopeAt('B', 1), QUERY, () => pending().start()) + expect(probes[0]?.isCurrent()).toBe(false) + + request.resolve(['a.ts']) + const stale = await settled(loaded) + expect(owner.commit(stale.lease, stale.value)).toBe('retired-generation') + }) + + it('publishes nothing and sends nothing further for a loader that stops on it', async () => { + const owner: Owner = new GenerationScopedRequestOwner() + const scope = scopeAt('w1', 1) + let sent = 0 + const firstLeg = pending() + const secondLeg = pending() + // Two legs, as the compare site has: the probe sits between them, so a superseded attempt never + // reaches the second one. + const attempt = + (leg: { start: () => Promise }) => + async (currency: RequestCurrency): Promise => { + await leg.start() + if (!currency.isCurrent()) { + return null + } + sent++ + return ['sent.ts'] + } + + const superseded = owner.load(scope, QUERY, attempt(firstLeg)) + owner.reset() + const live = owner.load(scope, QUERY, attempt(secondLeg)) + + firstLeg.resolve([]) + expect(await superseded).toBeNull() + expect(sent).toBe(0) + expect(owner.read(scope, QUERY)).toBeUndefined() + + secondLeg.resolve([]) + const lease = await settled(live) + expect(sent).toBe(1) + expect(owner.commit(lease.lease, lease.value)).toBe('committed') + expect(owner.read(scope, QUERY)).toEqual(['sent.ts']) + }) +}) + const mobileRoot = fileURLToPath(new URL('../..', import.meta.url)) const ownerModule = join(mobileRoot, 'src', 'transport', 'generation-scoped-request-owner') From cbd04704d698e217b070bbd1153279c75564e3f2 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 18:54:56 -0400 Subject: [PATCH 028/168] perf(relay): index and gate the credential cleanup sweeps that seq-scan relay_invites (#21301) * perf(relay): index and gate the credential cleanup sweeps that seq-scan relay_invites The credential cleanup ran every 30s in all 23 cells as well as the director. Both of its relay_invites passes matched columns no index covered, so each one seq-scanned the whole table inside the maintenance transaction: 56 calls/min fleet-wide, 129ms and 63ms typical and 57s at the tail, to return about one row every nine minutes. Adds partial indexes matching each sweep predicate, gives the cleanup the same owner as the assignment sweep, and reaps terminal invites after seven days so the table stops growing for the life of the database. Every index carries the schema-deferrable marker: an operator builds them with CREATE INDEX CONCURRENTLY, and the catalog pre-check skips them from then on. * perf(relay): index live bases and reap settled connection authorizations relay_connection_bases is the dominant cost in the cleanup transaction: 195 ms of the 268 ms average, with ~5,800 shared buffer hits per call even though it already uses relay_connection_bases_active_deadline. That index spans all 6.65M rows, and only a few hundred are ever active. Adds a partial index on the live rows alone, and reaps settled rows from relay_connection_bases and relay_direct_authorizations once their deadline is more than a day past. Both readers of either table require the row active/unconsumed and inside its deadline, and every deadline is set at most 30s past insert, so a settled row can never authorize anything again. The composite index stays: it is the only one covering active = 0, and it is what lets the drained reaper learn there is nothing to do from the index rather than the 1.5 GB heap. Measured at 200k rows, 5 buffers with it and 1,274 without. * test(relay): accept either bases index in the sweep plan assertion The negative assertion pinned a planner choice rather than the invariant: either index keeps the sweep off the 1.5 GB heap, and which one wins on cost is not something the test should fix. Matches how the same file already handles the two invite sweep indexes. Also names the column the authorization reaper actually measures, which is consumed_at rather than deadline. --- .../credential-cleanup-sweep-postgres.test.ts | 282 +++++++++++++++++ .../src/credential-store-cleanup.test.ts | 297 ++++++++++++++++++ cloud/apps/relay/src/credential-store.ts | 49 +++ cloud/apps/relay/src/database.ts | 31 ++ cloud/apps/relay/src/index.ts | 25 +- .../postgres-maintenance-sweep-plans.test.ts | 5 +- .../src/relay-schema-lock-targets.test.ts | 61 +++- .../relay/src/relay-sweep-schedule.test.ts | 19 ++ 8 files changed, 755 insertions(+), 14 deletions(-) create mode 100644 cloud/apps/relay/src/credential-cleanup-sweep-postgres.test.ts create mode 100644 cloud/apps/relay/src/credential-store-cleanup.test.ts diff --git a/cloud/apps/relay/src/credential-cleanup-sweep-postgres.test.ts b/cloud/apps/relay/src/credential-cleanup-sweep-postgres.test.ts new file mode 100644 index 00000000000..aa720a67b2d --- /dev/null +++ b/cloud/apps/relay/src/credential-cleanup-sweep-postgres.test.ts @@ -0,0 +1,282 @@ +import pg from 'pg' +import { afterAll, beforeEach, describe, expect, it } from 'vitest' +import { RelayCredentialStore, type RelayIdentity } from './credential-store.js' +import { openRelayDatabase, type RelayDatabase } from './database.js' + +// The outage this guards against: the credential cleanup ran every 30s in all 23 cells and both +// sweeps over relay_invites had no usable index, so each one seq-scanned the whole table inside the +// maintenance transaction. Only a real planner can show the partial indexes take that away, and +// only a real server has ctid. +const databaseUrl = process.env.ORCA_RELAY_TEST_POSTGRES_URL +const describePostgres = databaseUrl ? describe : describe.skip +const schema = 'relay_credential_sweep_test' + +const identity: RelayIdentity = { userId: 'user-1', relayHostId: 'abcdefghijklmnop' } +const DAY_MS = 24 * 60 * 60 * 1000 +const NOW = 100 * DAY_MS + +function scopedUrl(): string { + const url = new URL(databaseUrl!) + url.searchParams.set('options', `-c search_path=${schema}`) + return url.toString() +} + +async function onAdmin(operation: (client: pg.Client) => Promise): Promise { + const client = new pg.Client({ connectionString: databaseUrl }) + await client.connect() + try { + return await operation(client) + } finally { + await client.end() + } +} + +describePostgres('credential cleanup against PostgreSQL', () => { + let database: RelayDatabase + let store: RelayCredentialStore + const opened: RelayDatabase[] = [] + + beforeEach(async () => { + await onAdmin(async (client) => { + await client.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`) + await client.query(`CREATE SCHEMA ${schema}`) + }) + database = await openRelayDatabase({ databaseUrl: scopedUrl(), dataDir: '' }) + opened.push(database) + store = new RelayCredentialStore(database, () => NOW) + }) + + afterAll(async () => { + await Promise.all(opened.map((open) => open.close().catch(() => undefined))) + await onAdmin((client) => client.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`)) + }) + + async function seedInvites( + count: number, + state: string, + updatedAt: number, + expiresAt = NOW - DAY_MS + ): Promise { + await database.query( + `INSERT INTO relay_invites + (user_id, relay_host_id, relay_device_id, token_hash, state, attempt_count, + max_attempts, expires_at, created_at, updated_at) + SELECT ?, ?, 'device-' || n, 'token-' || ? || '-' || n, ?, 0, 3, ?, ?, ? + FROM generate_series(1, ?) AS n`, + [identity.userId, identity.relayHostId, state, state, expiresAt, updatedAt, updatedAt, count] + ) + } + + // Not through RelayDatabase: it routes anything that is not a SELECT to the row-count path, and + // EXPLAIN on an UPDATE is neither. + async function plan(sql: string, params: unknown[]): Promise { + const client = new pg.Client({ connectionString: scopedUrl() }) + await client.connect() + try { + let index = 0 + const result = await client.query( + `EXPLAIN ${sql.replace(/\?/g, () => `$${(index += 1)}`)}`, + params + ) + return result.rows.map((row) => String(row['QUERY PLAN'])).join('\n') + } finally { + await client.end() + } + } + + it('plans both invite sweeps as index scans instead of scanning the whole table', async () => { + // Production's shape: terminal invites outnumber live ones by orders of magnitude, which is + // what makes the partial predicates worth having. + await seedInvites(20_000, 'consumed', NOW) + for (const state of ['available', 'reserved', 'cooldown']) { + await seedInvites(200, state, NOW, NOW + DAY_MS) + } + await database.query( + `UPDATE relay_invites SET reservation_expires_at = ? WHERE state = 'reserved'`, + [NOW + 1] + ) + // Only ANALYZE makes the planner's row estimates real; without it a cold table looks tiny and + // a seq scan wins on any index. + await database.query(`ANALYZE relay_invites`) + + const expiry = await plan( + `UPDATE relay_invites SET state = 'expired' + WHERE expires_at <= ? AND state IN ('available', 'reserved', 'cooldown')`, + [NOW] + ) + const reservation = await plan( + `UPDATE relay_invites SET state = 'cooldown' + WHERE state = 'reserved' AND reservation_expires_at <= ? AND expires_at > ?`, + [NOW, NOW] + ) + + // Which of the two partial indexes serves the reservation pass is the planner's call: both + // predicates hold only live invites, so either one reads a handful of rows. The invariant is + // that neither pass reads the whole table any more. + for (const sweep of [expiry, reservation]) { + expect(sweep).not.toContain('Seq Scan on relay_invites') + expect(sweep).toMatch(/using relay_invites_sweep_(expiry|reservation)/) + } + expect(expiry).toContain('relay_invites_sweep_expiry') + }) + + it('plans the live-basis sweep off an index rather than the 1.5 GB heap', async () => { + // The shape that made this the most expensive statement in the sweep: 20,000 settled bases to + // 50 live ones, so the composite (active, deadline) index spans 400x the rows the sweep wants. + // Which index serves it is the planner's call, the same as for the two invite sweeps below. + await database.query( + `INSERT INTO relay_connection_bases + (basis_conn_id, user_id, relay_host_id, relay_device_id, owning_control_generation, + credential_kind, deadline, active, created_at) + SELECT 'settled-' || n, ?, ?, 'device-1', 1, 'invite', ?, 0, ? + FROM generate_series(1, 20000) AS n`, + [identity.userId, identity.relayHostId, NOW - 2 * DAY_MS, NOW - 2 * DAY_MS] + ) + await database.query( + `INSERT INTO relay_connection_bases + (basis_conn_id, user_id, relay_host_id, relay_device_id, owning_control_generation, + credential_kind, deadline, active, created_at) + SELECT 'live-' || n, ?, ?, 'device-1', 1, 'invite', ?, 1, ? + FROM generate_series(1, 50) AS n`, + [identity.userId, identity.relayHostId, NOW + 30_000, NOW] + ) + await database.query(`ANALYZE relay_connection_bases`) + + const sweep = await plan( + `UPDATE relay_connection_bases SET active = 0 WHERE active = 1 AND deadline <= ?`, + [NOW] + ) + + expect(sweep).not.toContain('Seq Scan on relay_connection_bases') + expect(sweep).toMatch(/using relay_connection_bases_(active|live)_deadline/) + }) + + it('plans the drained basis reaper off the composite index, not the heap', async () => { + // Why the composite index stays for now: it is the only one covering active = 0, and the case + // that needs it is the steady state, where every row is inside retention and the reaper must + // learn there is nothing to do. While the backlog drains the planner rightly prefers a bounded + // sequential scan, because it finds its 5,000 rows and stops; measured at 200k rows, the + // drained batch costs 5 buffers with this index and 1,274 without it. + await database.query( + `INSERT INTO relay_connection_bases + (basis_conn_id, user_id, relay_host_id, relay_device_id, owning_control_generation, + credential_kind, deadline, active, created_at) + SELECT 'settled-' || n, ?, ?, 'device-1', 1, 'invite', ?, 0, ? + FROM generate_series(1, 20000) AS n`, + [identity.userId, identity.relayHostId, NOW - 60_000, NOW - 60_000] + ) + await database.query(`ANALYZE relay_connection_bases`) + + const reaper = await plan( + `DELETE FROM relay_connection_bases WHERE ctid IN ( + SELECT ctid FROM relay_connection_bases WHERE active = ? AND deadline <= ? LIMIT 5000 + )`, + [0, NOW - DAY_MS] + ) + + expect(reaper).toContain('relay_connection_bases_active_deadline') + expect(reaper).not.toContain('Seq Scan on relay_connection_bases') + }) + + it('plans the pending-authorization and rate-window sweeps as index scans', async () => { + await database.query( + `INSERT INTO relay_direct_authorizations + (direct_auth_id, user_id, relay_host_id, relay_device_id, owning_control_generation, + deadline, consumed_at) + SELECT 'auth-' || n, ?, ?, 'device-1', 1, ?, ? + FROM generate_series(1, 20000) AS n`, + [identity.userId, identity.relayHostId, NOW - 1, NOW - 1] + ) + await database.query( + `INSERT INTO relay_rate_windows (scope_key, window_kind, window_started_at, count) + SELECT 'scope-' || n, 'invite-mint', ?, 1 FROM generate_series(1, 20000) AS n`, + [NOW] + ) + await database.query(`ANALYZE relay_direct_authorizations`) + await database.query(`ANALYZE relay_rate_windows`) + + const pending = await plan( + `UPDATE relay_direct_authorizations SET consumed_at = ? + WHERE consumed_at IS NULL AND deadline <= ?`, + [NOW, NOW] + ) + const windows = await plan(`DELETE FROM relay_rate_windows WHERE window_started_at < ?`, [ + NOW - DAY_MS + ]) + + expect(pending).toContain('relay_direct_authorizations_pending_deadline') + expect(pending).not.toContain('Seq Scan on relay_direct_authorizations') + expect(windows).toContain('relay_rate_windows_started') + expect(windows).not.toContain('Seq Scan on relay_rate_windows') + }) + + it('reaps settled bases and consumed authorizations through ctid', async () => { + await database.query( + `INSERT INTO relay_connection_bases + (basis_conn_id, user_id, relay_host_id, relay_device_id, owning_control_generation, + credential_kind, deadline, active, created_at) + SELECT 'settled-' || n, ?, ?, 'device-1', 1, 'invite', ?, 0, ? + FROM generate_series(1, 5002) AS n`, + [identity.userId, identity.relayHostId, NOW - 2 * DAY_MS, NOW - 2 * DAY_MS] + ) + await database.query( + `INSERT INTO relay_connection_bases + (basis_conn_id, user_id, relay_host_id, relay_device_id, owning_control_generation, + credential_kind, deadline, active, created_at) + VALUES ('live', ?, ?, 'device-1', 1, 'invite', ?, 1, ?)`, + [identity.userId, identity.relayHostId, NOW + 30_000, NOW] + ) + await database.query( + `INSERT INTO relay_direct_authorizations + (direct_auth_id, user_id, relay_host_id, relay_device_id, owning_control_generation, + deadline, consumed_at) + SELECT 'consumed-' || n, ?, ?, 'device-1', 1, ?, ? + FROM generate_series(1, 5002) AS n`, + [identity.userId, identity.relayHostId, NOW - 2 * DAY_MS, NOW - 2 * DAY_MS] + ) + await database.query( + `INSERT INTO relay_direct_authorizations + (direct_auth_id, user_id, relay_host_id, relay_device_id, owning_control_generation, + deadline, consumed_at) + VALUES ('pending', ?, ?, 'device-1', 1, ?, NULL)`, + [identity.userId, identity.relayHostId, NOW + 30_000] + ) + + await store.cleanup() + expect( + await database.query(`SELECT count(*) AS total FROM relay_connection_bases`) + ).toEqual([{ total: '3' }]) + expect( + await database.query(`SELECT count(*) AS total FROM relay_direct_authorizations`) + ).toEqual([{ total: '3' }]) + + await store.cleanup() + // Only the rows a reader could still accept are left. + expect( + await database.query(`SELECT basis_conn_id FROM relay_connection_bases`) + ).toEqual([{ basis_conn_id: 'live' }]) + expect( + await database.query(`SELECT direct_auth_id FROM relay_direct_authorizations`) + ).toEqual([{ direct_auth_id: 'pending' }]) + }) + + it('reaps terminal invites past retention through ctid, one bounded batch per cycle', async () => { + await seedInvites(5_002, 'consumed', NOW - 30 * DAY_MS) + await seedInvites(3, 'invalidated', NOW - 6 * DAY_MS) + await seedInvites(2, 'available', NOW - 400 * DAY_MS, NOW + DAY_MS) + + await store.cleanup() + expect(await database.query(`SELECT count(*) AS total FROM relay_invites`)).toEqual([ + { total: '7' } + ]) + + await store.cleanup() + // The two live invites and the three inside retention survive; the batch remainder is gone. + expect( + await database.query(`SELECT state, count(*) AS total FROM relay_invites GROUP BY state ORDER BY state`) + ).toEqual([ + { state: 'available', total: '2' }, + { state: 'invalidated', total: '3' } + ]) + }) +}) diff --git a/cloud/apps/relay/src/credential-store-cleanup.test.ts b/cloud/apps/relay/src/credential-store-cleanup.test.ts new file mode 100644 index 00000000000..ba9e7be7475 --- /dev/null +++ b/cloud/apps/relay/src/credential-store-cleanup.test.ts @@ -0,0 +1,297 @@ +import { describe, expect, it } from 'vitest' +import { RelayCredentialStore, type RelayIdentity } from './credential-store.js' +import { openInMemoryRelayDatabase, type RelayDatabase } from './database.js' + +const identity: RelayIdentity = { userId: 'user-1', relayHostId: 'abcdefghijklmnop' } +const DAY_MS = 24 * 60 * 60 * 1000 +const NOW = 100 * DAY_MS + +async function insertInvite( + database: RelayDatabase, + invite: { token: string; state: string; updatedAt: number; expiresAt?: number } +): Promise { + await database.query( + `INSERT INTO relay_invites + (user_id, relay_host_id, relay_device_id, token_hash, state, attempt_count, + max_attempts, expires_at, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + [ + identity.userId, + identity.relayHostId, + `device-${invite.token}`, + invite.token, + invite.state, + 0, + 3, + invite.expiresAt ?? NOW + DAY_MS, + invite.updatedAt, + invite.updatedAt + ] + ) +} + +async function remainingTokens(database: RelayDatabase): Promise { + const rows = await database.query(`SELECT token_hash FROM relay_invites ORDER BY token_hash`) + return rows.map((row) => String(row.token_hash)) +} + +async function insertBasis( + database: RelayDatabase, + basis: { id: string; active: number; deadline: number } +): Promise { + await database.query( + `INSERT INTO relay_connection_bases + (basis_conn_id, user_id, relay_host_id, relay_device_id, owning_control_generation, + credential_kind, deadline, active, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, + [ + basis.id, + identity.userId, + identity.relayHostId, + 'device-1', + 1, + 'invite', + basis.deadline, + basis.active, + NOW + ] + ) +} + +async function insertDirectAuthorization( + database: RelayDatabase, + auth: { id: string; deadline: number; consumedAt: number | null } +): Promise { + await database.query( + `INSERT INTO relay_direct_authorizations + (direct_auth_id, user_id, relay_host_id, relay_device_id, owning_control_generation, + deadline, consumed_at) + VALUES (?, ?, ?, ?, ?, ?, ?)`, + [auth.id, identity.userId, identity.relayHostId, 'device-1', 1, auth.deadline, auth.consumedAt] + ) +} + +async function remainingIds(database: RelayDatabase, table: string, column: string): Promise { + const rows = await database.query(`SELECT ${column} FROM ${table} ORDER BY ${column}`) + return rows.map((row) => String(row[column])) +} + +describe('credential cleanup invite reaper', () => { + it('deletes terminal invites past retention and keeps everything else', async () => { + const database = await openInMemoryRelayDatabase() + const store = new RelayCredentialStore(database, () => NOW) + const stale = NOW - 8 * DAY_MS + const recent = NOW - 6 * DAY_MS + for (const state of ['expired', 'consumed', 'invalidated']) { + await insertInvite(database, { token: `stale-${state}`, state, updatedAt: stale }) + await insertInvite(database, { token: `recent-${state}`, state, updatedAt: recent }) + } + + await store.cleanup() + + expect(await remainingTokens(database)).toEqual([ + 'recent-consumed', + 'recent-expired', + 'recent-invalidated' + ]) + await database.close() + }) + + it('never deletes an invite that a reader could still consume, however old', async () => { + // Retention is measured on updated_at, and a long-lived available invite has an old one. The + // state filter is what keeps the reaper from deleting a credential still in use. + const database = await openInMemoryRelayDatabase() + const store = new RelayCredentialStore(database, () => NOW) + const ancient = NOW - 400 * DAY_MS + for (const state of ['available', 'reserved', 'cooldown']) { + await insertInvite(database, { + token: `live-${state}`, + state, + updatedAt: ancient, + expiresAt: NOW + DAY_MS + }) + } + + await store.cleanup() + + expect(await remainingTokens(database)).toEqual(['live-available', 'live-cooldown', 'live-reserved']) + await database.close() + }) + + it('bounds one cycle to a single batch and drains the rest on later cycles', async () => { + const database = await openInMemoryRelayDatabase() + const store = new RelayCredentialStore(database, () => NOW) + const stale = NOW - 30 * DAY_MS + for (let index = 0; index < 5_002; index += 1) { + await insertInvite(database, { + token: `consumed-${String(index).padStart(5, '0')}`, + state: 'consumed', + updatedAt: stale + }) + } + + await store.cleanup() + expect(await remainingTokens(database)).toHaveLength(2) + + await store.cleanup() + expect(await remainingTokens(database)).toEqual([]) + await database.close() + }) + + it('still expires credentials the sweep owns, and only those past their deadline', async () => { + // The reaper runs after the sweep in the same call, so this pins that adding it did not + // displace any of the five state transitions the sweep is there for. + const database = await openInMemoryRelayDatabase() + const store = new RelayCredentialStore(database, () => NOW) + await insertInvite(database, { + token: 'lapsed', + state: 'available', + updatedAt: NOW, + expiresAt: NOW - 1 + }) + await insertInvite(database, { + token: 'current', + state: 'available', + updatedAt: NOW, + expiresAt: NOW + DAY_MS + }) + await database.query( + `UPDATE relay_invites SET state = ?, reservation_expires_at = ? WHERE token_hash = ?`, + ['reserved', NOW - 1, 'current'] + ) + await database.query( + `INSERT INTO relay_connection_bases + (basis_conn_id, user_id, relay_host_id, relay_device_id, owning_control_generation, + credential_kind, deadline, active, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?)`, + [ + 'basis-lapsed', identity.userId, identity.relayHostId, 'device-1', 1, 'invite', NOW - 1, 1, NOW, + 'basis-live', identity.userId, identity.relayHostId, 'device-1', 1, 'invite', NOW + 1, 1, NOW + ] + ) + await store.recordDirectAuthorization({ + ...identity, + relayDeviceId: 'device-1', + directAuthId: 'direct-lapsed', + owningControlGeneration: 1, + deadline: NOW - 1 + }) + await store.recordDirectAuthorization({ + ...identity, + relayDeviceId: 'device-1', + directAuthId: 'direct-live', + owningControlGeneration: 1, + deadline: NOW + 1 + }) + await database.query( + `INSERT INTO relay_rate_windows (scope_key, window_kind, window_started_at, count) + VALUES (?, ?, ?, ?), (?, ?, ?, ?)`, + ['scope', 'invite-mint', NOW - 2 * DAY_MS, 1, 'scope', 'invite-mint', NOW - 1, 1] + ) + + await store.cleanup() + + expect( + await database.query(`SELECT token_hash, state FROM relay_invites ORDER BY token_hash`) + ).toEqual([ + { token_hash: 'current', state: 'cooldown' }, + { token_hash: 'lapsed', state: 'expired' } + ]) + expect( + await database.query(`SELECT basis_conn_id, active FROM relay_connection_bases ORDER BY basis_conn_id`) + ).toEqual([ + { basis_conn_id: 'basis-lapsed', active: 0 }, + { basis_conn_id: 'basis-live', active: 1 } + ]) + expect( + await database.query( + `SELECT direct_auth_id FROM relay_direct_authorizations + WHERE consumed_at IS NULL ORDER BY direct_auth_id` + ) + ).toEqual([{ direct_auth_id: 'direct-live' }]) + expect(await database.query(`SELECT window_started_at FROM relay_rate_windows`)).toEqual([ + { window_started_at: NOW - 1 } + ]) + await database.close() + }) + + it('reaps connection bases whose deadline passed over a day ago, and nothing else', async () => { + // Retention is measured on deadline, and both readers of a basis require deadline >= now, so a + // deadline a day in the past is already unusable however the active flag reads. The active = 0 + // clause is what keeps the batch an index range, not what makes the row safe to delete. + const database = await openInMemoryRelayDatabase() + const store = new RelayCredentialStore(database, () => NOW) + await insertBasis(database, { id: 'stale-inactive', active: 0, deadline: NOW - 2 * DAY_MS }) + await insertBasis(database, { id: 'recent-inactive', active: 0, deadline: NOW - 60_000 }) + // A long-lived splice: still active hours after the 30s deadline it was created with. The + // sweep deactivates it this cycle and the reaper takes it in the same call, which is safe + // precisely because no reader would have accepted it since its deadline passed. + await insertBasis(database, { id: 'stale-active', active: 1, deadline: NOW - 400 * DAY_MS }) + await insertBasis(database, { id: 'live-active', active: 1, deadline: NOW + DAY_MS }) + + await store.cleanup() + + expect(await remainingIds(database, 'relay_connection_bases', 'basis_conn_id')).toEqual([ + 'live-active', + 'recent-inactive' + ]) + // The one row a reader can still use is untouched, active flag included. + expect( + await database.query( + `SELECT active FROM relay_connection_bases WHERE basis_conn_id = 'live-active'` + ) + ).toEqual([{ active: 1 }]) + await database.close() + }) + + it('reaps consumed direct authorizations past retention and never a pending one', async () => { + const database = await openInMemoryRelayDatabase() + const store = new RelayCredentialStore(database, () => NOW) + await insertDirectAuthorization(database, { + id: 'stale-consumed', + deadline: NOW - 2 * DAY_MS, + consumedAt: NOW - 2 * DAY_MS + }) + await insertDirectAuthorization(database, { + id: 'recent-consumed', + deadline: NOW - 60_000, + consumedAt: NOW - 60_000 + }) + await insertDirectAuthorization(database, { + id: 'pending-ancient', + deadline: NOW + DAY_MS, + consumedAt: null + }) + + await store.cleanup() + + expect(await remainingIds(database, 'relay_direct_authorizations', 'direct_auth_id')).toEqual([ + 'pending-ancient', + 'recent-consumed' + ]) + await database.close() + }) + + it('bounds each table to one batch per cycle', async () => { + const database = await openInMemoryRelayDatabase() + const store = new RelayCredentialStore(database, () => NOW) + for (let index = 0; index < 5_001; index += 1) { + const id = String(index).padStart(5, '0') + await insertBasis(database, { id: `basis-${id}`, active: 0, deadline: NOW - 2 * DAY_MS }) + await insertDirectAuthorization(database, { + id: `auth-${id}`, + deadline: NOW - 2 * DAY_MS, + consumedAt: NOW - 2 * DAY_MS + }) + } + + await store.cleanup() + expect(await remainingIds(database, 'relay_connection_bases', 'basis_conn_id')).toHaveLength(1) + expect(await remainingIds(database, 'relay_direct_authorizations', 'direct_auth_id')).toHaveLength(1) + + await store.cleanup() + expect(await remainingIds(database, 'relay_connection_bases', 'basis_conn_id')).toEqual([]) + expect(await remainingIds(database, 'relay_direct_authorizations', 'direct_auth_id')).toEqual([]) + await database.close() + }) +}) diff --git a/cloud/apps/relay/src/credential-store.ts b/cloud/apps/relay/src/credential-store.ts index a5697e8c699..8d1281bb3c5 100644 --- a/cloud/apps/relay/src/credential-store.ts +++ b/cloud/apps/relay/src/credential-store.ts @@ -12,6 +12,17 @@ const CREDENTIAL_GRACE_MS = 24 * 60 * 60 * 1000 // tolerance at exactly inviteTtlMs; issuing under the ceiling keeps pairing // working for clients whose clocks trail the cell by up to this margin. const INVITE_ISSUE_SKEW_MARGIN_MS = 30 * 1000 +// Terminal invites are read by nothing: every reader re-checks expiry and state at read time, so +// the row only serves the audit trail, which relay_audit_events already keeps. A week is long +// enough to answer a support question about a pairing that failed. +const TERMINAL_INVITE_RETENTION_MS = 7 * 24 * 60 * 60 * 1000 +// Why: both readers of a connection basis and of a direct authorization require it still +// active/unconsumed AND inside its deadline, and every deadline is set at most 30s past insert, so +// a settled row can never authorize anything again. A day is margin for forensics, not for reads. +const INACTIVE_AUTHORIZATION_RETENTION_MS = 24 * 60 * 60 * 1000 +// Bounded so one cycle cannot hold row locks or grow WAL without limit; the backlog drains over +// however many cycles it takes. +const REAP_BATCH_ROWS = 5000 export type RelayIdentity = { userId: string; relayHostId: string } export type CredentialReservation = RelayIdentity & { @@ -643,6 +654,44 @@ export class RelayCredentialStore { [now - 24 * 60 * 60 * 1000] ) }) + await this.reapSettledCredentials(now) + } + + // Outside the sweep transaction on purpose: each delete is idempotent and independent of the + // state transitions above, so batching them in would only hold their row locks for longer. + private async reapSettledCredentials(now: number): Promise { + await this.reapBatch( + 'relay_invites', + 'state IN (?, ?, ?) AND updated_at <= ?', + ['expired', 'consumed', 'invalidated', now - TERMINAL_INVITE_RETENTION_MS] + ) + // deadline, not created_at: it is the second column of relay_connection_bases_active_deadline, + // so once the backlog is drained this batch learns there is nothing left to do from the index + // instead of the 1.5 GB heap. Both readers reject a passed deadline, so a day past one is + // unusable whatever the active flag says. + await this.reapBatch('relay_connection_bases', 'active = ? AND deadline <= ?', [ + 0, + now - INACTIVE_AUTHORIZATION_RETENTION_MS + ]) + // consumed_at, not deadline: consumption is what settles this row, and it can happen well + // before the deadline, so measuring from it retains the row for the full window either way. + await this.reapBatch( + 'relay_direct_authorizations', + 'consumed_at IS NOT NULL AND consumed_at <= ?', + [now - INACTIVE_AUTHORIZATION_RETENTION_MS] + ) + } + + // ctid/rowid, not the primary key: the physical address lets the delete re-find exactly the batch + // the subquery located instead of re-matching the predicate per row. + private async reapBatch(table: string, predicate: string, params: unknown[]): Promise { + const address = this.database.dialect === 'sqlite' ? 'rowid' : 'ctid' + await this.database.query( + `DELETE FROM ${table} WHERE ${address} IN ( + SELECT ${address} FROM ${table} WHERE ${predicate} LIMIT ${REAP_BATCH_ROWS} + )`, + params + ) } private async installStatusWith( diff --git a/cloud/apps/relay/src/database.ts b/cloud/apps/relay/src/database.ts index 344fee47ab2..a62bb35798f 100644 --- a/cloud/apps/relay/src/database.ts +++ b/cloud/apps/relay/src/database.ts @@ -100,6 +100,18 @@ CREATE TABLE IF NOT EXISTS relay_invites ( CREATE INDEX IF NOT EXISTS relay_invites_device ON relay_invites(user_id, relay_host_id, relay_device_id); +-- schema-deferrable: created out of band, so a boot that cannot take the lock must retry +-- Why: the credential sweep matches (state, expires_at) every cycle while invites in a terminal +-- state accumulate for the life of the database. Unindexed it seq-scans the whole table inside the +-- maintenance transaction. Partial, so the index holds only the states the sweep can act on. +CREATE INDEX IF NOT EXISTS relay_invites_sweep_expiry + ON relay_invites(expires_at) WHERE state IN ('available', 'reserved', 'cooldown'); + +-- schema-deferrable: created out of band, so a boot that cannot take the lock must retry +-- Why: the second sweep pass matches (state, reservation_expires_at) over the same table. +CREATE INDEX IF NOT EXISTS relay_invites_sweep_reservation + ON relay_invites(reservation_expires_at) WHERE state = 'reserved'; + CREATE TABLE IF NOT EXISTS relay_devices ( user_id TEXT NOT NULL, relay_host_id TEXT NOT NULL, @@ -163,6 +175,13 @@ CREATE TABLE IF NOT EXISTS relay_connection_bases ( CREATE INDEX IF NOT EXISTS relay_connection_bases_active_deadline ON relay_connection_bases(active, deadline); +-- schema-deferrable: created out of band, so a boot that cannot take the lock must retry +-- Why: the index above spans every row, and inactive bases outnumber live ones by ~6.6M to a few +-- hundred, so the sweep still walked ~283 MB of index to find them. This one holds only the rows +-- the sweep can act on. Keeping both: the composite is also what makes the reaper an index range. +CREATE INDEX IF NOT EXISTS relay_connection_bases_live_deadline + ON relay_connection_bases(deadline) WHERE active = 1; + CREATE TABLE IF NOT EXISTS relay_direct_authorizations ( direct_auth_id TEXT PRIMARY KEY, user_id TEXT NOT NULL, @@ -173,6 +192,12 @@ CREATE TABLE IF NOT EXISTS relay_direct_authorizations ( consumed_at BIGINT ); +-- schema-deferrable: created out of band, so a boot that cannot take the lock must retry +-- Why: the sweep expires pending authorizations by (consumed_at IS NULL, deadline), and consumed +-- rows are never deleted. Partial, so the index stays the size of the pending set. +CREATE INDEX IF NOT EXISTS relay_direct_authorizations_pending_deadline + ON relay_direct_authorizations(deadline) WHERE consumed_at IS NULL; + CREATE TABLE IF NOT EXISTS relay_confirm_results ( user_id TEXT NOT NULL, relay_host_id TEXT NOT NULL, @@ -560,6 +585,12 @@ CREATE TABLE IF NOT EXISTS relay_rate_windows ( PRIMARY KEY (scope_key, window_kind, window_started_at) ); +-- schema-deferrable: created out of band, so a boot that cannot take the lock must retry +-- Why: window_started_at is the PRIMARY KEY's last column, so the sweep's 24h retention delete +-- cannot use it and seq-scans instead. +CREATE INDEX IF NOT EXISTS relay_rate_windows_started + ON relay_rate_windows(window_started_at); + CREATE TABLE IF NOT EXISTS relay_migration_leases ( user_id TEXT NOT NULL, relay_host_id TEXT NOT NULL, diff --git a/cloud/apps/relay/src/index.ts b/cloud/apps/relay/src/index.ts index 8e7b6a56941..36a89b9de0a 100644 --- a/cloud/apps/relay/src/index.ts +++ b/cloud/apps/relay/src/index.ts @@ -46,14 +46,19 @@ const { ready, cellIncarnation } = createRelayServer(config, database) -const cleanupTimer = setInterval( - () => - void runRelayBackgroundOperation( - () => store.cleanup(), - '[orca-relay] credential cleanup failed' - ), - 30_000 -) +// Same owner as the assignment sweep: the cleanup only expires credentials that every reader +// already re-checks at read time, so running it in all 23 cells multiplied one table scan by 23 +// without changing any answer. +const cleanupTimer = roleOwnsAssignmentMaintenance(config.role) + ? setInterval( + () => + void runRelayBackgroundOperation( + () => store.cleanup(), + '[orca-relay] credential cleanup failed' + ), + jitteredSweepIntervalMs(30_000) + ) + : null const assignmentCleanupTimer = roleOwnsAssignmentMaintenance(config.role) ? setInterval(() => { void runAssignmentCleanup(assignments) @@ -82,7 +87,7 @@ const migrationInventoryTimer = roleOwnsAssignmentMaintenance(config.role) }, '[orca-relay] migration inventory failed') }, 5 * 60_000) : null -cleanupTimer.unref() +cleanupTimer?.unref() assignmentCleanupTimer?.unref() inventorySnapshotTimer?.unref() migrationInventoryTimer?.unref() @@ -128,7 +133,7 @@ server.listen(config.port, () => { }) const shutdown = (): void => { - clearInterval(cleanupTimer) + if (cleanupTimer) clearInterval(cleanupTimer) if (assignmentCleanupTimer) clearInterval(assignmentCleanupTimer) if (inventorySnapshotTimer) clearInterval(inventorySnapshotTimer) if (migrationInventoryTimer) clearInterval(migrationInventoryTimer) diff --git a/cloud/apps/relay/src/postgres-maintenance-sweep-plans.test.ts b/cloud/apps/relay/src/postgres-maintenance-sweep-plans.test.ts index a8d0e1e3bf9..de8573a0d6c 100644 --- a/cloud/apps/relay/src/postgres-maintenance-sweep-plans.test.ts +++ b/cloud/apps/relay/src/postgres-maintenance-sweep-plans.test.ts @@ -56,6 +56,9 @@ describePostgres('PostgreSQL maintenance sweep plans', () => { const plan = result.rows.map((row) => String(row['QUERY PLAN'])).join('\n') expect(plan).not.toMatch(/Seq Scan on relay_connection_bases/) - expect(plan).toMatch(/relay_connection_bases_active_deadline/) + // Either index keeps the sweep off the table. It used to be the composite one; the partial + // relay_connection_bases_live_deadline now wins on cost, because it spans only the live rows + // rather than all ~6.6M, and that is the improvement, not a regression in this invariant. + expect(plan).toMatch(/relay_connection_bases_(active|live)_deadline/) }) }) diff --git a/cloud/apps/relay/src/relay-schema-lock-targets.test.ts b/cloud/apps/relay/src/relay-schema-lock-targets.test.ts index 52ae85ccec0..26348bb7d9c 100644 --- a/cloud/apps/relay/src/relay-schema-lock-targets.test.ts +++ b/cloud/apps/relay/src/relay-schema-lock-targets.test.ts @@ -17,9 +17,28 @@ import { relayPostgresSchemaStatements } from './database.js' // CREATE INDEX CONCURRENTLY first, then add it to SCHEMA and update this list. const GOLDEN_LOCK_TAKING: SchemaLockTarget[] = [ { kind: 'index', table: 'relay_invites', name: 'relay_invites_device', skipWhen: 'present' }, + { kind: 'index', table: 'relay_invites', name: 'relay_invites_sweep_expiry', skipWhen: 'present' }, + { + kind: 'index', + table: 'relay_invites', + name: 'relay_invites_sweep_reservation', + skipWhen: 'present' + }, { kind: 'index', table: 'relay_devices', name: 'relay_devices_current_hash', skipWhen: 'present' }, { kind: 'index', table: 'relay_devices', name: 'relay_devices_grace_hash', skipWhen: 'present' }, { kind: 'index', table: 'relay_connection_bases', name: 'relay_connection_bases_active_deadline', skipWhen: 'present' }, + { + kind: 'index', + table: 'relay_connection_bases', + name: 'relay_connection_bases_live_deadline', + skipWhen: 'present' + }, + { + kind: 'index', + table: 'relay_direct_authorizations', + name: 'relay_direct_authorizations_pending_deadline', + skipWhen: 'present' + }, { kind: 'index', table: 'relay_assignment_region_preferences', @@ -85,6 +104,7 @@ const GOLDEN_LOCK_TAKING: SchemaLockTarget[] = [ name: 'relay_control_connection_reservation_assignment', skipWhen: 'present' }, + { kind: 'index', table: 'relay_rate_windows', name: 'relay_rate_windows_started', skipWhen: 'present' }, { kind: 'index', table: 'relay_assignment_migrations', name: 'relay_assignment_migrations_active', skipWhen: 'present' }, { kind: 'index', @@ -224,16 +244,51 @@ describe('relay boot-time lock targets', () => { } }) - it('marks both activity-lease migrations deferrable, and nothing else', () => { - // The two statements a lock timeout must not turn into a crash loop, and the only two: every + it('marks the out-of-band sweep indexes and the activity-lease migrations deferrable, and nothing else', () => { + // The statements a lock timeout must not turn into a crash loop, and the only ones: every // other statement still fails the boot loudly, which is what keeps the marker meaningful. const deferrable = relayPostgresSchemaStatements().filter(schemaDeferrable) - expect(deferrable.map(sqlWithoutComments)).toEqual([ + expect(deferrable.map((statement) => sqlWithoutComments(statement).replace(/\s+/g, ' '))).toEqual([ + "CREATE INDEX IF NOT EXISTS relay_invites_sweep_expiry ON relay_invites(expires_at) WHERE state IN ('available', 'reserved', 'cooldown')", + "CREATE INDEX IF NOT EXISTS relay_invites_sweep_reservation ON relay_invites(reservation_expires_at) WHERE state = 'reserved'", + 'CREATE INDEX IF NOT EXISTS relay_connection_bases_live_deadline ON relay_connection_bases(deadline) WHERE active = 1', + 'CREATE INDEX IF NOT EXISTS relay_direct_authorizations_pending_deadline ON relay_direct_authorizations(deadline) WHERE consumed_at IS NULL', + 'CREATE INDEX IF NOT EXISTS relay_rate_windows_started ON relay_rate_windows(window_started_at)', 'DROP INDEX IF EXISTS relay_assignment_activity_expiry', 'ALTER TABLE relay_assignment_activity_leases SET (fillfactor = 70)' ]) }) + it('derives a target for a partial index, WHERE clause and all', () => { + // The pre-check reads the index name and table from the head of the statement, so a trailing + // WHERE is invisible to it. Asserted because the sweep indexes depend on that: a parser that + // gave a partial index no target would send it unchecked on every boot. + const partial = relayPostgresSchemaStatements().filter((statement) => + /^CREATE\s+INDEX\b[\s\S]*\bWHERE\b/i.test(sqlWithoutComments(statement)) + ) + expect(partial.map(schemaLockTarget)).toEqual([ + { kind: 'index', table: 'relay_invites', name: 'relay_invites_sweep_expiry', skipWhen: 'present' }, + { + kind: 'index', + table: 'relay_invites', + name: 'relay_invites_sweep_reservation', + skipWhen: 'present' + }, + { + kind: 'index', + table: 'relay_connection_bases', + name: 'relay_connection_bases_live_deadline', + skipWhen: 'present' + }, + { + kind: 'index', + table: 'relay_direct_authorizations', + name: 'relay_direct_authorizations_pending_deadline', + skipWhen: 'present' + } + ]) + }) + it('no longer creates an index on the column every control renewal writes', () => { // The regression this drop exists to prevent: re-adding it would make ~471 renewals/s non-HOT // again. A CREATE anywhere in the schema naming that index fails here. diff --git a/cloud/apps/relay/src/relay-sweep-schedule.test.ts b/cloud/apps/relay/src/relay-sweep-schedule.test.ts index b9965ae29ff..79f65a92582 100644 --- a/cloud/apps/relay/src/relay-sweep-schedule.test.ts +++ b/cloud/apps/relay/src/relay-sweep-schedule.test.ts @@ -52,4 +52,23 @@ describe('sweep schedule jitter', () => { expect(cleanup?.[1]).toBe('jitteredSweepIntervalMs(30_000)') }) + + it('jitters the credential cleanup tick', () => { + const source = readFileSync(new URL('./index.ts', import.meta.url), 'utf8') + const cleanup = /'\[orca-relay\] credential cleanup failed'\s*\),\s*([^\n]*?)\n/.exec(source) + + expect(cleanup?.[1]).toBe('jitteredSweepIntervalMs(30_000)') + }) + + // A census, not a list of the timers that happen to be gated today: an ungated sweep runs in + // every cell as well as the director, which multiplies one table scan by the fleet size. + it('gates every periodic sweep in index.ts on the maintenance role', () => { + const source = readFileSync(new URL('./index.ts', import.meta.url), 'utf8') + const timers = source.match(/setInterval\(/g) ?? [] + const gated = + source.match(/roleOwnsAssignmentMaintenance\(config\.role\)\s*\?\s*setInterval\(/g) ?? [] + + expect(timers.length).toBeGreaterThan(0) + expect(gated.length).toBe(timers.length) + }) }) From 40b22305084a700c2a8860aa85377e851a427a65 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 18:58:25 -0400 Subject: [PATCH 029/168] test(mobile): typecheck the test files on a ratchet, and pin the reply enums where tsc looks (#21298) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(mobile): move the last six reply-enum pins where tsc looks mobile/tsconfig.json excludes *.test.ts, so a `Record` coverage record in a schema test is never typechecked: the two that existed (SshConnectionStatus, GitHubProjectOwnerType) checked nothing, and the four closed enums beside them had only a doc citation of the host type. Each arm list moves into its schema module as hostUnionArms(), which #21269 introduced for the same reason, and each test iterates the exported list instead of holding its own copy: - SSH_CONNECTION_STATUS to SshConnectionStatus - PROJECT_OWNER_TYPE to GitHubProjectOwnerType - DETAIL_FILE_STATUS to GitHubPRFile['status'] - PUSH_TEST_REFUSAL_REASONS and PUSH_REGISTER_REFUSAL_REASONS to the refusal arms of MobilePushTestResult and MobilePushRegisterResult - SETUP_RUN_POLICIES to SetupRunPolicy openEnum's parameter widens from a non-empty tuple to `readonly string[]` so a hostUnionArms list can feed it. z.enum already accepts the same, so the tuple constraint only excluded callers zod itself takes; behaviour unchanged. Twelve mutations prove the pins: dropping one arm and adding a bogus one each fail mobile tsc in all six places. Zero goldens move, the schemas' behaviour being unchanged, and the 21 recording suites pass at the existing baseline. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): fix the type errors in eighteen test files Found by typechecking the tests for the first time (see the config that follows). All mechanical, none weakens a product type: - 67 `act(() => vi.advanceTimersByTime(...))` callbacks return VitestUtils where act wants void, so each becomes a block. The async ones await only a genuinely promise-returning call, so no extra microtask tick is introduced. - Four fixtures were stale against a product type that gained a required member: MobileViewState.alwaysShowDefaultBranch, PrSidebarData.checksError, the branch-compare summary's errorMessage, and SessionOptionDescriptor's transport, which #20884 added precisely so a producer could not inherit the wrong lane's rendering by omission. - `getLastConnectedAt` on the shared relay fake was typed `() => null`, which refused the timestamp two escalation suites assign to it. - Two holders used before assignment take `!`, one `advance!.kind === ...` becomes `advance?.kind`, one widened status arm takes `as const`, and the Expo notification fixture keeps `data` required because the dismissal cases assign through it. 631 test files pass, 6222 tests, unchanged. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): typecheck the test files, on a ratchet mobile/tsconfig.json excludes *.test.ts so Metro never compiles tests into the release bundle, and vitest transpiles without checking types. Nothing had ever typechecked a mobile test, which is why a `Record` pin written in one proved nothing and why 144 of the 630 test files had drifted. tsconfig.test.json is that program with the tests put back, behind `typecheck:tests`. Four files stay out: they import the desktop main process or src/shared/child-process, which are written against @types/node, and this program's libs are React Native's, where setTimeout answers a number rather than a NodeJS.Timeout. Pulling that graph in reports ~280 errors about the desktop rather than about mobile; vitest runs those four under Node, which is where they belong. The CI gate is a ratchet rather than the raw typecheck, modelled on check-ts-nocheck-ratchet.mjs: 126 files still fail, so the gate freezes that set and fails when a file that checks today stops checking, or when a baseline entry starts checking and was not pruned. The list may only shrink. Why not zero: 180 of the remaining 510 errors are one seam — tests locate mocked react-native components by string name, which `ElementType` does not admit — and closing it means either 180 casts or a global JSX declaration for the mocked names. That is a design decision, not a mechanical fix, so it is left for a follow-up rather than made here. The rest are smaller clusters of the same kind: vi.fn mocks assigned into typed slots, call-arg tuple indexing, and createElement props fixtures. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile-recorder): correct the corpus counts and the salvage claim The oracle section still quoted the corpus as 368 scenarios and 727 goldens; it is 393 and 778, and the three replay suites report 781 tests. Each number now names the command that measures it. "No golden carries one" was the load-bearing error: 44 goldens carry a recorded `reply-salvage` today, starting with the push-test unknown-reason scenario #21176 added for exactly that purpose. The paragraph claimed the observation pins an absence when on those families it pins a recorded drop. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): pin the tests-typecheck ratchet's parser The gate reads tsc's output, and tsc indents the "Overload 1 of 2, ..." detail under an error. Counting those as filenames would write unparseable entries into the baseline and leave the gate unprunable, so the parser is pinned on that shape as well as on the added/stale diff. Written against the gate itself: it flagged this file before the directive it carried was removed, which is the end-to-end proof the spawn half works. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): await the timer advances the act() rewrite dropped Rewriting `await act(async () => vi.advanceTimersByTimeAsync(n))` into a braced body left the returned promise floating at 27 sites, so the advance was no longer ordered before the assertions that follow it. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): unshadow MobileHostCard's .tsx suite A wildcard `include` keeps only the higher-priority extension, so MobileHostCard.test.tsx sat outside every tsc program while MobileHostCard.test.ts existed beside it. Its one error is the same react-test-renderer seam its sibling is baselined for. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): census every test file into the typecheck program The ratchet diffs only files that error, so a test excluded from tsconfig.test.json or shadowed by a sibling extension left the gate silently. Every *.test.ts(x) on disk must now be in the program or named in TESTS_OUTSIDE_PROGRAM with its reason. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(shared): make the enum helpers refuse the ways they can prove nothing openEnum takes a `const` T so a bare literal keeps its arms rather than widening to string. hostUnionArms blocks inference of U with NoInfer and defaults it to never, so a call that omits the host union — where the record would only pin itself — no longer compiles. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): describe the census and correct the baseline count Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): give the push fixture cast its SAFETY rationale Widening the pre-existing cast made the changed-code gate attribute it as a new finding. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): build the push fixtures as typed notifications Replaces the `as unknown as` cast with Expo's own types, filling FirebaseRemoteMessage and its notification once in two builders, and passes the data payload in rather than mutating through an optional member. Typing the fixture showed one assertion comparing the scheduled content against the whole arriving content, which only held while the cast let the fixture omit the two members the presenter drops; it now names the four members the presenter forwards. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): keep the grouped-question advance read non-optional `advance?.kind` let an absent advance take the null-draft branch instead of failing. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): run the tests-typecheck ratchet on Windows Spawns tsc's JS entry on this Node instead of the node_modules/.bin shim, which is a POSIX shell script that Windows resolves to tsc.CMD and then appends .exe to. Parsed paths are normalised to POSIX so a Windows run does not read every baseline entry as both stale and added. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): close the ratchet's @ts-nocheck hole and read tsc once tsc exits 0 on a @ts-nocheck file, so a baselined test could be "fixed" with one line, pruned, and never checked again; the census now names any program test file whose leading comment carries the directive. `--noEmit --listFiles` answers both questions in one pass, so the gate spawns tsc once rather than twice. Corrects the two stale counts, and states hostUnionArms' real reason for living in the schema module now that tests are typechecked. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- .github/workflows/mobile.yml | 7 + mobile/README.md | 13 + mobile/package.json | 2 + .../scripts/check-tests-typecheck-ratchet.mjs | 347 ++++++++++++++++++ .../check-tests-typecheck-ratchet.test.ts | 174 +++++++++ ...> MobileHostCard-truthful-status.test.tsx} | 0 .../new-workspace-reply-schema.test.ts | 6 +- .../components/new-workspace-reply-schema.ts | 18 +- ...use-new-worktree-drawer-navigation.test.ts | 8 +- mobile/src/hooks/use-now.test.ts | 16 +- .../NotificationDeliverySection.test.tsx | 2 +- .../android-foreground-push.test.ts | 104 +++++- .../notification-reply-schema.test.ts | 13 + .../notification-reply-schema.ts | 48 ++- .../src/session/MobileNativeChatView.test.ts | 64 +++- ...mobile-structured-grouped-question.test.ts | 5 +- .../session/mobile-terminal-records.test.ts | 2 +- ...use-mobile-native-chat-answer-send.test.ts | 44 ++- .../use-mobile-native-chat-controller.test.ts | 1 + .../use-mobile-native-chat-drafts.test.ts | 44 ++- ...use-mobile-native-chat-file-search.test.ts | 44 ++- ...use-mobile-native-chat-input-lease.test.ts | 20 +- .../use-mobile-native-chat-stop.test.ts | 20 +- .../use-throttled-latest-value.test.ts | 8 +- .../mobile-pr-chip-summary.test.ts | 2 +- ...bile-source-control-primary-action.test.ts | 1 + .../task-project-board-reply-schema.test.ts | 12 +- .../tasks/task-project-board-reply-schema.ts | 9 +- .../task-provider-entity-reply-schema.test.ts | 12 +- .../task-provider-entity-reply-schema.ts | 22 +- .../workspace-source-reply-schema.test.ts | 19 +- .../tasks/workspace-source-reply-schema.ts | 29 +- .../use-buffered-terminal-drafts.test.tsx | 2 +- .../src/test-support/rpc-recording/README.md | 17 +- .../mobile-endpoint-supervisor-test-fakes.ts | 3 +- .../worktree/workspace-view-settings.test.ts | 1 + mobile/tests-typecheck-baseline.txt | 131 +++++++ mobile/tsconfig.test.json | 18 + src/shared/zod-salvage.ts | 17 +- 39 files changed, 1120 insertions(+), 185 deletions(-) create mode 100644 mobile/scripts/check-tests-typecheck-ratchet.mjs create mode 100644 mobile/scripts/check-tests-typecheck-ratchet.test.ts rename mobile/src/components/{MobileHostCard.test.tsx => MobileHostCard-truthful-status.test.tsx} (100%) create mode 100644 mobile/tests-typecheck-baseline.txt create mode 100644 mobile/tsconfig.test.json diff --git a/.github/workflows/mobile.yml b/.github/workflows/mobile.yml index 1e94add89ad..730129d674d 100644 --- a/.github/workflows/mobile.yml +++ b/.github/workflows/mobile.yml @@ -96,6 +96,13 @@ jobs: - name: Typecheck run: pnpm typecheck + # Why a ratchet and not the raw typecheck: mobile/tsconfig.json excludes test files, so until + # tsconfig.test.json existed nothing checked them, and at introduction 127 of the 632 had + # drifted. This fails when a test file that checks today stops checking, when a test leaves + # the program, and on @ts-nocheck; the baseline may only shrink. + - name: Typecheck tests (ratchet) + run: pnpm run check:tests-typecheck + - name: Test run: pnpm test diff --git a/mobile/README.md b/mobile/README.md index e78c2c410bf..be17a867702 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -135,11 +135,24 @@ Run these checks before committing mobile terminal changes: ```bash cd mobile pnpm exec tsc --noEmit +pnpm run check:tests-typecheck pnpm lint cd .. pnpm typecheck:node ``` +`tsc --noEmit` reads `tsconfig.json`, which excludes test files so Metro never bundles them. +`tsconfig.test.json` puts them back, and `pnpm run typecheck:tests` shows their errors in full. +`check:tests-typecheck` is the gate over it: a ratchet against `tests-typecheck-baseline.txt`, the +127 test files that do not typecheck yet. It fails when a file that checks today stops checking, +and when a baseline entry starts checking (prune it with +`node scripts/check-tests-typecheck-ratchet.mjs --prune`). The list may only shrink. + +The same gate censuses the program first: every `*.test.ts(x)` on disk must be in it, or named in +the script's `TESTS_OUTSIDE_PROGRAM` with a reason. Without that, a test excluded from +`tsconfig.test.json` — or a `Foo.test.tsx` shadowed by a `Foo.test.ts` beside it, which a wildcard +`include` drops for the higher-priority extension — would leave the ratchet silently. + ## Protocol Version Compatibility Mobile and desktop talk over a versioned protocol. Because mobile updates lag desktop by 24-48h via the App Store, both sides exchange version numbers on `status.get` so a genuinely incompatible combo can hard-block instead of silently misbehaving. diff --git a/mobile/package.json b/mobile/package.json index d86c0d524ef..c4a977cd459 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -10,6 +10,8 @@ "postinstall": "node scripts/build-terminal-webview-engine.mjs && node scripts/build-mermaid-webview-engine.mjs", "test": "vitest run", "typecheck": "tsc --noEmit", + "typecheck:tests": "tsc --noEmit -p tsconfig.test.json", + "check:tests-typecheck": "node scripts/check-tests-typecheck-ratchet.mjs", "lint": "oxlint", "format": "oxfmt --write .", "format:check": "oxfmt --check .", diff --git a/mobile/scripts/check-tests-typecheck-ratchet.mjs b/mobile/scripts/check-tests-typecheck-ratchet.mjs new file mode 100644 index 00000000000..f82e5c1a278 --- /dev/null +++ b/mobile/scripts/check-tests-typecheck-ratchet.mjs @@ -0,0 +1,347 @@ +import { spawnSync } from 'node:child_process' +import fs from 'node:fs' +import { createRequire } from 'node:module' +import path from 'node:path' +import process from 'node:process' +import { pathToFileURL } from 'node:url' + +// Ratchet gate for the mobile test typecheck. +// +// mobile/tsconfig.json excludes *.test.ts so Metro never compiles tests into the release bundle, +// and vitest transpiles without typechecking. Nothing checked a mobile test until tsconfig.test.json +// existed, so 144 of the 632 test files had accumulated type errors — overwhelmingly one seam, the +// react-test-renderer / mocked-react-native pair, whose fix is a test-support typing decision rather +// than 587 local edits. This check freezes that set and fails when a test file that typechecks today +// stops doing so. The baseline may only shrink. + +const BASELINE_PATH = 'tests-typecheck-baseline.txt' +const PROJECT = 'tsconfig.test.json' +const ERROR_LINE = /^(\S.*?)\(\d+,\d+\): error TS\d+:/ + +// The only test files allowed to sit outside the program, and why. Everything else on disk must be +// in it: the error diff below sees a file only once it errors, so an excluded or shadowed test +// disappears from this gate silently. +export const TESTS_OUTSIDE_PROGRAM = new Map([ + [ + 'scripts/rpc-recording-pin-guard.test.ts', + 'Node-side: imports the desktop main process, checked against @types/node rather than RN libs' + ], + [ + 'src/tasks/agent-launch-mobile-replay.test.ts', + 'Node-side: imports the desktop main process, checked against @types/node rather than RN libs' + ], + [ + 'src/tasks/mobile-agent-launch-architecture.test.ts', + 'Node-side: imports the desktop main process, checked against @types/node rather than RN libs' + ], + [ + 'src/transport/mobile-relay-browser-cancel-budget.test.ts', + 'Node-side: imports src/shared/child-process, checked against @types/node rather than RN libs' + ] +]) + +// tsc prints the host's own separator; the baseline stores POSIX, so a Windows run would otherwise +// read every entry as both stale and added. +const toPosix = (filePath) => filePath.replaceAll('\\', '/') + +export function parseFailingFiles(tscOutput) { + const files = new Set() + for (const line of tscOutput.split('\n')) { + const matched = ERROR_LINE.exec(line) + if (matched) { + files.add(toPosix(matched[1])) + } + } + return [...files].sort() +} + +export function collectTestFilesOnDisk(root = process.cwd()) { + const found = [] + const walk = (dir) => { + for (const entry of fs.readdirSync(path.join(root, dir), { withFileTypes: true })) { + if (entry.name === 'node_modules' || entry.name.startsWith('.')) { + continue + } + const rel = dir ? `${dir}/${entry.name}` : entry.name + if (entry.isDirectory()) { + walk(rel) + } else if (/\.test\.tsx?$/.test(entry.name)) { + found.push(rel) + } + } + } + walk('') + return found.sort() +} + +// --listFiles prints one absolute real path per line into the same stream as the diagnostics; a +// diagnostic carries `(line,col): error` and a path relative to cwd, so neither filter can take the +// other's lines. +export function parseProgramTestFiles(tscOutput, realRoot) { + const prefix = `${toPosix(realRoot).replace(/\/$/, '')}/` + return [ + ...new Set( + tscOutput + .split('\n') + .map((line) => toPosix(line.trim())) + .filter((line) => /\.test\.tsx?$/.test(line) && line.startsWith(prefix)) + .map((line) => line.slice(prefix.length)) + ) + ].sort() +} + +// A baselined test could otherwise be "fixed" with one `@ts-nocheck`, pruned, and never checked +// again: tsc exits 0 on such a file and nothing else here would notice. +export function hasTsNocheckDirective(source) { + for (const line of source.split('\n')) { + const text = line.trim() + if (text === '') { + continue + } + if (!text.startsWith('//') && !text.startsWith('/*') && !text.startsWith('*')) { + return false + } + if (text.includes('@ts-nocheck')) { + return true + } + } + return false +} + +export function findTsNocheckFiles(root, files) { + return files + .filter((file) => hasTsNocheckDirective(fs.readFileSync(path.join(root, file), 'utf8'))) + .sort() +} + +export function diffCensus(onDisk, inProgram, allowed = TESTS_OUTSIDE_PROGRAM) { + const program = new Set(inProgram) + const allow = allowed instanceof Map ? allowed : new Map(allowed.map((e) => [e, ''])) + const disk = new Set(onDisk) + return { + missing: [...disk].filter((entry) => !program.has(entry) && !allow.has(entry)).sort(), + staleAllowance: [...allow.keys()] + .filter((entry) => !disk.has(entry) || program.has(entry)) + .sort() + } +} + +export function parseBaseline(text) { + return new Set( + text + .split('\n') + .map((line) => line.trim()) + .filter((line) => line && !line.startsWith('#')) + ) +} + +export function diffBaseline(current, baseline) { + const cur = new Set(current) + const base = baseline instanceof Set ? baseline : new Set(baseline) + return { + added: [...cur].filter((entry) => !base.has(entry)).sort(), + stale: [...base].filter((entry) => !cur.has(entry)).sort() + } +} + +// Run tsc's JS entry on this Node rather than the node_modules/.bin shim, which is a POSIX shell +// script: on Windows the shim is tsc.CMD and an extensionless path gets .exe appended. +function runTsc(root, args) { + const entry = createRequire(import.meta.url).resolve('typescript/lib/tsc.js') + const result = spawnSync(process.execPath, [entry, ...args], { + cwd: root, + encoding: 'utf8', + shell: false, + maxBuffer: 64 * 1024 * 1024 + }) + if (result.error) { + throw result.error + } + return result +} + +// tsc exits non-zero on type errors, which is the expected state here, so only a crash is fatal. +// One pass answers both questions: --listFiles names the program, the diagnostics name the failures. +export function collectTypecheckPass(root = process.cwd()) { + const result = runTsc(root, ['--noEmit', '--listFiles', '-p', PROJECT]) + const output = `${result.stdout ?? ''}${result.stderr ?? ''}` + return { + failing: parseFailingFiles(output), + programTestFiles: parseProgramTestFiles(output, fs.realpathSync(root)) + } +} + +export function collectCurrentFailingFiles(root = process.cwd()) { + return collectTypecheckPass(root).failing +} + +function printAddedFailure(added) { + for (const entry of added) { + console.error(`::error::Test file no longer typechecks: ${entry}`) + } + console.error('') + console.error('╭────────────────────────────────────────────────────────────────────────────╮') + console.error('│ ❌ mobile tests typecheck ratchet failed — a test file stopped checking. │') + console.error('╰────────────────────────────────────────────────────────────────────────────╯') + console.error('') + console.error(` ${added.length} test file(s) newly fail \`tsc -p ${PROJECT}\`:`) + console.error('') + for (const entry of added) { + console.error(` • ${entry}`) + } + console.error('') + console.error(' See the errors with: pnpm --filter orca-mobile typecheck:tests') + console.error('') + console.error(' A type-level pin in an unchecked test proves nothing, which is the whole reason') + console.error(' this gate exists. Fix the test rather than adding it to the baseline.') + console.error('') +} + +function printStaleFailure(stale) { + for (const entry of stale) { + console.error(`::error::Stale tests-typecheck baseline entry (prune it): ${entry}`) + } + console.error('') + console.error('╭────────────────────────────────────────────────────────────────────────────╮') + console.error( + '│ ⚠️ tests-typecheck baseline is out of date — nice work fixing a test! │' + ) + console.error('╰────────────────────────────────────────────────────────────────────────────╯') + console.error('') + console.error(` ${stale.length} baseline entr(y/ies) now typecheck clean.`) + console.error(' The baseline may only shrink, so these must be removed to keep them checked:') + console.error('') + for (const entry of stale) { + console.error(` • ${entry}`) + } + console.error('') + console.error( + ` ✅ Fix it (one command): pnpm --filter orca-mobile check:tests-typecheck --prune` + ) + console.error('') +} + +function printCensusFailure(missing, staleAllowance, nocheck = []) { + for (const entry of missing) { + console.error(`::error::Test file is not in the typecheck program: ${entry}`) + } + for (const entry of staleAllowance) { + console.error(`::error::Stale TESTS_OUTSIDE_PROGRAM entry: ${entry}`) + } + for (const entry of nocheck) { + console.error(`::error::Test file opts out of checking with @ts-nocheck: ${entry}`) + } + console.error('') + console.error('╭────────────────────────────────────────────────────────────────────────────╮') + console.error('│ ❌ mobile tests typecheck census failed — a test file is unchecked. │') + console.error('╰────────────────────────────────────────────────────────────────────────────╯') + console.error('') + if (missing.length > 0) { + console.error(` ${missing.length} test file(s) on disk are outside \`tsc -p ${PROJECT}\`:`) + console.error('') + for (const entry of missing) { + console.error(` • ${entry}`) + } + console.error('') + console.error(' Usual causes: an added `exclude` entry, or a `Foo.test.tsx` shadowed by a') + console.error(' `Foo.test.ts` beside it — a wildcard `include` keeps only the higher-priority') + console.error(' extension, so the .tsx silently leaves the program. Rename one, or exclude it') + console.error(' on purpose by adding it to TESTS_OUTSIDE_PROGRAM with its reason.') + console.error('') + } + if (staleAllowance.length > 0) { + console.error(` ${staleAllowance.length} TESTS_OUTSIDE_PROGRAM entr(y/ies) no longer apply`) + console.error(' (the file is gone, or it is in the program now). Remove them:') + console.error('') + for (const entry of staleAllowance) { + console.error(` • ${entry}`) + } + console.error('') + } + if (nocheck.length > 0) { + console.error(` ${nocheck.length} test file(s) carry @ts-nocheck, which makes tsc exit 0 on`) + console.error(' them. That would let a baselined file be pruned and never checked again:') + console.error('') + for (const entry of nocheck) { + console.error(` • ${entry}`) + } + console.error('') + } +} + +export function main(root = process.cwd()) { + const baselineFile = path.join(root, BASELINE_PATH) + if (!fs.existsSync(baselineFile)) { + console.error( + `::error::Missing mobile/${BASELINE_PATH}. Generate it with: node scripts/check-tests-typecheck-ratchet.mjs --init` + ) + return 1 + } + const pass = collectTypecheckPass(root) + const census = diffCensus(collectTestFilesOnDisk(root), pass.programTestFiles) + const nocheck = findTsNocheckFiles(root, pass.programTestFiles) + if (census.missing.length > 0 || census.staleAllowance.length > 0 || nocheck.length > 0) { + printCensusFailure(census.missing, census.staleAllowance, nocheck) + return 1 + } + + const baseline = parseBaseline(fs.readFileSync(baselineFile, 'utf8')) + const current = pass.failing + const { added, stale } = diffBaseline(current, baseline) + + if (added.length > 0) { + printAddedFailure(added) + if (stale.length > 0) { + printStaleFailure(stale) + } + return 1 + } + if (stale.length > 0) { + printStaleFailure(stale) + return 1 + } + console.log( + `mobile tests typecheck ratchet OK — ${pass.programTestFiles.length} test file(s) in the program (${TESTS_OUTSIDE_PROGRAM.size} excluded on purpose, none @ts-nocheck), ${current.length} grandfathered file(s), every other test file checks.` + ) + return 0 +} + +function writeBaseline(root, entries) { + const header = [ + '# Test files that do NOT yet typecheck under mobile/tsconfig.test.json.', + '# This is a RATCHET: the list may only SHRINK. Do NOT add entries to get CI green —', + '# an unchecked test is one whose type-level pins prove nothing.', + '# Regenerate/prune: node scripts/check-tests-typecheck-ratchet.mjs --prune', + '' + ].join('\n') + fs.writeFileSync(path.join(root, BASELINE_PATH), `${header}${entries.join('\n')}\n`) +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + const root = process.cwd() + const arg = process.argv[2] + if (arg === '--init') { + const entries = collectCurrentFailingFiles(root) + writeBaseline(root, entries) + console.log(`Wrote mobile/${BASELINE_PATH} with ${entries.length} entries.`) + process.exit(0) + } + if (arg === '--prune') { + const current = new Set(collectCurrentFailingFiles(root)) + const baseline = parseBaseline(fs.readFileSync(path.join(root, BASELINE_PATH), 'utf8')) + const kept = [...baseline].filter((entry) => current.has(entry)).sort() + const newlyAdded = [...current].filter((entry) => !baseline.has(entry)) + writeBaseline(root, kept) + console.log( + `Pruned baseline to ${kept.length} entries (removed ${baseline.size - kept.length}).` + ) + if (newlyAdded.length > 0) { + console.error( + `::error::--prune does not add entries; ${newlyAdded.length} test file(s) newly fail — fix those.` + ) + process.exit(1) + } + process.exit(0) + } + process.exit(main(root)) +} diff --git a/mobile/scripts/check-tests-typecheck-ratchet.test.ts b/mobile/scripts/check-tests-typecheck-ratchet.test.ts new file mode 100644 index 00000000000..3876894b350 --- /dev/null +++ b/mobile/scripts/check-tests-typecheck-ratchet.test.ts @@ -0,0 +1,174 @@ +import fs from 'node:fs' +import path from 'node:path' +import { describe, expect, it } from 'vitest' +// Only the pure functions are under test; the tsc spawn is proven end to end by CI. +import { + diffBaseline, + diffCensus, + hasTsNocheckDirective, + parseBaseline, + parseFailingFiles, + parseProgramTestFiles, + TESTS_OUTSIDE_PROGRAM +} from './check-tests-typecheck-ratchet.mjs' + +describe('the failing-file parser', () => { + it('names each file once however many errors it carries', () => { + const output = [ + 'src/a.test.ts(12,5): error TS2345: Argument of type x.', + 'src/a.test.ts(19,1): error TS2322: Type y.', + 'src/b.test.tsx(3,3): error TS18047: z is possibly null.' + ].join('\n') + expect(parseFailingFiles(output)).toEqual(['src/a.test.ts', 'src/b.test.tsx']) + }) + + // tsc indents the "Overload 1 of 2, ..." detail under its error; counting those as files would + // put unparseable entries in the baseline and make the gate unprunable. + it('ignores the indented detail lines tsc prints under an error', () => { + const output = [ + 'src/a.test.ts(12,5): error TS2769: No overload matches this call.', + " Overload 1 of 2, '(callback: () => Promise): Promise', gave the following error.", + ' Type VitestUtils is missing the following properties.' + ].join('\n') + expect(parseFailingFiles(output)).toEqual(['src/a.test.ts']) + }) + + // tsc prints the host separator. Left as-is, a Windows run would read every baseline entry as + // both stale and added, and the gate would be unpassable rather than wrong in one direction. + it("normalises the Windows separators tsc prints to the baseline's POSIX ones", () => { + const output = [ + 'src\\session\\a.test.ts(12,5): error TS2345: Argument of type x.', + 'scripts\\b.test.tsx(3,3): error TS18047: z is possibly null.' + ].join('\n') + expect(parseFailingFiles(output)).toEqual(['scripts/b.test.tsx', 'src/session/a.test.ts']) + }) + + it('answers nothing for a clean run', () => { + expect(parseFailingFiles('')).toEqual([]) + }) +}) + +describe('the baseline diff', () => { + it('drops comments and blanks when reading the baseline', () => { + expect([...parseBaseline('# header\n\nsrc/a.test.ts\n src/b.test.ts \n')]).toEqual([ + 'src/a.test.ts', + 'src/b.test.ts' + ]) + }) + + it('reports a newly failing file as added and a newly clean one as stale', () => { + expect( + diffBaseline(['src/a.test.ts', 'src/c.test.ts'], ['src/a.test.ts', 'src/b.test.ts']) + ).toEqual({ added: ['src/c.test.ts'], stale: ['src/b.test.ts'] }) + }) + + it('reports neither when the set is unchanged, which is the green path', () => { + expect(diffBaseline(['src/a.test.ts'], ['src/a.test.ts'])).toEqual({ added: [], stale: [] }) + }) +}) + +describe('the program census', () => { + const allow = new Map([['src/node-side.test.ts', 'imports the desktop main process']]) + + // The error diff sees a file only once it errors, so without this an excluded test is silent. + it('names a test file that is on disk but outside the program', () => { + expect( + diffCensus(['src/a.test.ts', 'src/b.test.ts'], ['src/a.test.ts'], allow).missing + ).toEqual(['src/b.test.ts']) + }) + + // A wildcard include keeps only the higher-priority extension, so a .tsx beside a .test.ts of the + // same basename leaves the program with no config change at all. This is the case that hid + // MobileHostCard.test.tsx. + it('names a .tsx shadowed by a .test.ts of the same basename', () => { + expect( + diffCensus(['src/Card.test.ts', 'src/Card.test.tsx'], ['src/Card.test.ts'], new Map()).missing + ).toEqual(['src/Card.test.tsx']) + }) + + it('stays quiet for a file excluded on purpose', () => { + expect(diffCensus(['src/node-side.test.ts'], [], allow)).toEqual({ + missing: [], + staleAllowance: [] + }) + }) + + it('reports an allowance whose file is gone, so the list cannot rot', () => { + expect(diffCensus([], [], allow).staleAllowance).toEqual(['src/node-side.test.ts']) + }) + + it('reports an allowance whose file is back in the program', () => { + expect( + diffCensus(['src/node-side.test.ts'], ['src/node-side.test.ts'], allow).staleAllowance + ).toEqual(['src/node-side.test.ts']) + }) + + it('stays quiet when every file on disk is in the program', () => { + expect(diffCensus(['src/a.test.ts'], ['src/a.test.ts'], new Map())).toEqual({ + missing: [], + staleAllowance: [] + }) + }) + + // The allow-list and the tsconfig exclude are two lists of the same four files; drift between + // them would either break the build or re-open the hole silently. + it('matches tsconfig.test.json exclude entry for entry', () => { + const root = path.join(import.meta.dirname, '..') + const config = fs.readFileSync(path.join(root, 'tsconfig.test.json'), 'utf8') + const excluded = [...config.matchAll(/"([^"]+\.test\.tsx?)"/g)].map((match) => match[1]) + expect(excluded.sort()).toEqual([...TESTS_OUTSIDE_PROGRAM.keys()].sort()) + for (const entry of TESTS_OUTSIDE_PROGRAM.keys()) { + expect(fs.existsSync(path.join(root, entry))).toBe(true) + } + }) +}) + +// One `tsc --noEmit --listFiles` pass answers both questions, so both parsers read the same stream. +describe('the single-pass output split', () => { + const output = [ + '/repo/mobile/node_modules/typescript/lib/lib.es2020.d.ts', + '/repo/mobile/src/session/a.test.ts', + '/repo/mobile/src/session/a.ts', + '/repo/mobile/scripts/b.test.tsx', + 'src/session/a.test.ts(12,5): error TS2345: Argument of type x.', + 'scripts/b.test.tsx(3,3): error TS18047: z is possibly null.' + ].join('\n') + + it('takes only the listed test paths as the program', () => { + expect(parseProgramTestFiles(output, '/repo/mobile')).toEqual([ + 'scripts/b.test.tsx', + 'src/session/a.test.ts' + ]) + }) + + it('takes only the diagnostics as the failures, from that same stream', () => { + expect(parseFailingFiles(output)).toEqual(['scripts/b.test.tsx', 'src/session/a.test.ts']) + }) + + it('strips a Windows root the same way it strips the separators', () => { + expect(parseProgramTestFiles('C:\\repo\\mobile\\src\\a.test.ts', 'C:\\repo\\mobile')).toEqual([ + 'src/a.test.ts' + ]) + }) +}) + +// tsc exits 0 on a @ts-nocheck file, so without this a baselined test could be "fixed" with one +// line, pruned off the baseline, and never checked again. +describe('the @ts-nocheck guard', () => { + it('sees the directive in a leading line comment', () => { + expect(hasTsNocheckDirective('// @ts-nocheck\nimport { it } from "vitest"\n')).toBe(true) + }) + + it('sees it in a leading block comment', () => { + expect(hasTsNocheckDirective('/**\n * @ts-nocheck\n */\nexport {}\n')).toBe(true) + }) + + // TypeScript only honours it before the first statement, so neither should this. + it('ignores it once code has started', () => { + expect(hasTsNocheckDirective('import { it } from "vitest"\n// @ts-nocheck\n')).toBe(false) + }) + + it('stays quiet for an ordinary header comment', () => { + expect(hasTsNocheckDirective('// Tests the reply schema.\nexport {}\n')).toBe(false) + }) +}) diff --git a/mobile/src/components/MobileHostCard.test.tsx b/mobile/src/components/MobileHostCard-truthful-status.test.tsx similarity index 100% rename from mobile/src/components/MobileHostCard.test.tsx rename to mobile/src/components/MobileHostCard-truthful-status.test.tsx diff --git a/mobile/src/components/new-workspace-reply-schema.test.ts b/mobile/src/components/new-workspace-reply-schema.test.ts index 2d0862f3441..ebacbdfc27e 100644 --- a/mobile/src/components/new-workspace-reply-schema.test.ts +++ b/mobile/src/components/new-workspace-reply-schema.test.ts @@ -2,7 +2,8 @@ import { describe, expect, it } from 'vitest' import type { z } from 'zod' import { newWorkspaceRepoHooksSchema, - newWorkspaceUiTrustSchema + newWorkspaceUiTrustSchema, + SETUP_RUN_POLICIES } from './new-workspace-reply-schema' function reads(schema: z.ZodType, value: unknown): T { @@ -44,8 +45,9 @@ describe('the drawer requires only what it reads unguarded', () => { }) describe('the setup run policy is a closed enum with the call site defaulting it', () => { + // The arm list is pinned to SetupRunPolicy in the schema module, where tsc looks. it('keeps each arm the drawer compares against', () => { - for (const policy of ['ask', 'run-by-default', 'skip-by-default'] as const) { + for (const policy of SETUP_RUN_POLICIES) { expect( reads(newWorkspaceRepoHooksSchema, { source: null, setupRunPolicy: policy }).setupRunPolicy ).toBe(policy) diff --git a/mobile/src/components/new-workspace-reply-schema.ts b/mobile/src/components/new-workspace-reply-schema.ts index 22e8b3f4557..f5c3b04103b 100644 --- a/mobile/src/components/new-workspace-reply-schema.ts +++ b/mobile/src/components/new-workspace-reply-schema.ts @@ -1,10 +1,18 @@ import { z } from 'zod' -import { salvagedOptional, salvagingRecord } from '../../../src/shared/zod-salvage' +import type { SetupRunPolicy } from '../../../src/shared/orca-yaml-hook-types' +import { hostUnionArms, salvagedOptional, salvagingRecord } from '../../../src/shared/zod-salvage' // The New Workspace drawer's own two reads. Checked against `repo.hooks` // (src/main/runtime/rpc/methods/repo.ts:190 → runtime.getRepoHooks) and `ui.get` // (src/main/runtime/rpc/methods/client-ui.ts:60), which answers `{ ui }` and nothing else. +// Pinned to the host's own union through hostUnionArms: an arm added or dropped host-side fails tsc. +export const SETUP_RUN_POLICIES = hostUnionArms({ + ask: true, + 'run-by-default': true, + 'skip-by-default': true +}) + /** * The repo's setup hook, as the drawer decorates its advanced section with it. * @@ -19,7 +27,8 @@ import { salvagedOptional, salvagingRecord } from '../../../src/shared/zod-salva * site's `?? 'run-by-default'` still doing the defaulting: the only two comparisons against it are * `!== 'skip-by-default'` and `=== 'ask'`, so an arm this build does not know behaves exactly as * main's unrecognised string did. The arms are the host's `SetupRunPolicy` - * (src/shared/orca-yaml-hook-types.ts:1), which is what `getEffectiveSetupRunPolicy` answers. `setupTrust` is nullable as well as optional because the + * (src/shared/orca-yaml-hook-types.ts:1), which is what `getEffectiveSetupRunPolicy` answers, and + * they are pinned to it above. `setupTrust` is nullable as well as optional because the * `components-setup-ask` fixture sends an explicit `null` — salvaging that as a drop would move a * `normal` golden for a reply the host really sends. */ @@ -36,10 +45,7 @@ export const newWorkspaceRepoHooksSchema = z.looseObject({ .nullable() ), source: z.string().nullable(), - setupRunPolicy: salvagedOptional( - 'setupRunPolicy', - z.enum(['ask', 'run-by-default', 'skip-by-default']) - ), + setupRunPolicy: salvagedOptional('setupRunPolicy', z.enum(SETUP_RUN_POLICIES)), setupTrust: salvagedOptional( 'setupTrust', z.looseObject({ contentHash: z.string(), scriptContent: z.string() }).nullable() diff --git a/mobile/src/components/use-new-worktree-drawer-navigation.test.ts b/mobile/src/components/use-new-worktree-drawer-navigation.test.ts index 347e309c5e0..83c4a03f326 100644 --- a/mobile/src/components/use-new-worktree-drawer-navigation.test.ts +++ b/mobile/src/components/use-new-worktree-drawer-navigation.test.ts @@ -45,7 +45,9 @@ describe('useNewWorktreeDrawerNavigation', () => { expect(nav.current.drawerView).toBe('transition') expect(nav.current.formSheetVisible).toBe(true) - act(() => vi.advanceTimersByTime(500)) + act(() => { + vi.advanceTimersByTime(500) + }) expect(nav.current.drawerView).toBe('agent') expect(nav.current.formSheetVisible).toBe(false) }) @@ -54,7 +56,9 @@ describe('useNewWorktreeDrawerNavigation', () => { const nav = renderNavigation(true) act(() => nav.current.openSourceDrawer()) act(() => nav.current.transitionDrawer('form')) - act(() => vi.advanceTimersByTime(500)) + act(() => { + vi.advanceTimersByTime(500) + }) expect(nav.current.drawerView).toBe('form') expect(nav.current.formSheetVisible).toBe(true) diff --git a/mobile/src/hooks/use-now.test.ts b/mobile/src/hooks/use-now.test.ts index 14264b513ee..9248cac492f 100644 --- a/mobile/src/hooks/use-now.test.ts +++ b/mobile/src/hooks/use-now.test.ts @@ -59,23 +59,31 @@ describe('useNow', () => { it('ticks while active, pauses in the background, and refreshes immediately on resume', () => { expect(latest).toBe(1_000) - act(() => vi.advanceTimersByTime(1_000)) + act(() => { + vi.advanceTimersByTime(1_000) + }) expect(latest).toBe(2_000) changeAppState('background') - act(() => vi.advanceTimersByTime(5_000)) + act(() => { + vi.advanceTimersByTime(5_000) + }) expect(latest).toBe(2_000) changeAppState('active') expect(latest).toBe(7_000) - act(() => vi.advanceTimersByTime(1_000)) + act(() => { + vi.advanceTimersByTime(1_000) + }) expect(latest).toBe(8_000) }) it('stops while disabled and refreshes immediately when re-enabled', () => { act(() => renderer?.update(createElement(Harness, { enabled: false }))) - act(() => vi.advanceTimersByTime(5_000)) + act(() => { + vi.advanceTimersByTime(5_000) + }) expect(latest).toBe(1_000) act(() => renderer?.update(createElement(Harness, { enabled: true }))) diff --git a/mobile/src/notifications/NotificationDeliverySection.test.tsx b/mobile/src/notifications/NotificationDeliverySection.test.tsx index 8b7971b6086..f51614a821f 100644 --- a/mobile/src/notifications/NotificationDeliverySection.test.tsx +++ b/mobile/src/notifications/NotificationDeliverySection.test.tsx @@ -14,7 +14,7 @@ vi.mock('react-native', () => ({ it('shows only phone-specific controls while desktop owns category eligibility', () => { const onChange = vi.fn() - let renderer: ReturnType + let renderer!: ReturnType act(() => { renderer = create( createElement(NotificationDeliverySection, { value: DEFAULT_NOTIFICATION_DELIVERY, onChange }) diff --git a/mobile/src/notifications/android-foreground-push.test.ts b/mobile/src/notifications/android-foreground-push.test.ts index cad381e7ee9..2172dd5e339 100644 --- a/mobile/src/notifications/android-foreground-push.test.ts +++ b/mobile/src/notifications/android-foreground-push.test.ts @@ -1,5 +1,9 @@ import { beforeEach, expect, it, vi } from 'vitest' -import type { Notification } from 'expo-notifications' +import type { + FirebaseRemoteMessageNotification, + Notification, + NotificationTrigger +} from 'expo-notifications' import { startAndroidForegroundPushPresentation } from './android-foreground-push' const mocks = vi.hoisted(() => ({ @@ -19,26 +23,84 @@ vi.mock('expo-notifications', () => ({ scheduleNotificationAsync: mocks.schedule })) -function notification(trigger: unknown = { type: 'push', remoteMessage: { notification: null } }) { +// Expo's remote-message types are wide and fully required; the two builders below fill them once +// so the fixtures below can be plain `Notification` values rather than assertions. +const REMOTE_NOTIFICATION: FirebaseRemoteMessageNotification = { + body: null, + bodyLocalizationArgs: null, + bodyLocalizationKey: null, + channelId: null, + clickAction: null, + color: null, + eventTime: null, + icon: null, + imageUrl: null, + lightSettings: null, + link: null, + localOnly: false, + notificationCount: null, + notificationPriority: null, + sound: null, + sticky: false, + tag: null, + ticker: null, + title: null, + titleLocalizationArgs: null, + titleLocalizationKey: null, + usesDefaultLightSettings: false, + usesDefaultSound: false, + usesDefaultVibrateSettings: false, + vibrateTimings: null, + visibility: null +} + +function pushTrigger(remote: FirebaseRemoteMessageNotification | null): NotificationTrigger { return { + type: 'push', + remoteMessage: { + collapseKey: null, + data: {}, + from: null, + messageId: 'message-1', + messageType: null, + notification: remote, + originalPriority: 1, + priority: 1, + sentTime: 0, + to: null, + ttl: 0 + } + } +} + +const ORCA_PUSH_DATA: Record = { + hostFingerprint: 'host', + notificationId: 'event', + notificationEpoch: 'epoch', + notificationSeq: '3', + paneKey: 'pane', + channelId: 'orca-desktop' +} + +function notification( + trigger: NotificationTrigger = pushTrigger(null), + data: Record = ORCA_PUSH_DATA +): Notification { + return { + date: 0, request: { identifier: 'message-1', trigger, content: { title: 'Test notification', + subtitle: null, body: '', + categoryIdentifier: null, sound: 'default', - data: { - hostFingerprint: 'host', - notificationId: 'event', - notificationEpoch: 'epoch', - notificationSeq: '3', - paneKey: 'pane', - channelId: 'orca-desktop' - } + data } } - } as unknown as Notification + } } beforeEach(() => { @@ -52,9 +114,16 @@ it('presents a title-only data push with its original identity, routing and chan const incoming = notification() mocks.receive(incoming) await vi.waitFor(() => expect(mocks.schedule).toHaveBeenCalledOnce()) + // The four members the presenter forwards, named: it rebuilds content rather than passing the + // arriving object through, so comparing against the whole fixture would only hold by accident. expect(mocks.schedule).toHaveBeenCalledWith({ identifier: incoming.request.identifier, - content: incoming.request.content, + content: { + title: incoming.request.content.title, + body: incoming.request.content.body, + data: incoming.request.content.data, + sound: 'default' + }, trigger: { channelId: 'orca-desktop' } }) stop() @@ -64,18 +133,15 @@ it('presents a title-only data push with its original identity, routing and chan it('does not reschedule its own local notification or normal provider notifications', () => { startAndroidForegroundPushPresentation() mocks.receive(notification(null)) - mocks.receive(notification({ type: 'channel', channelId: 'orca-desktop' })) - mocks.receive(notification({ type: 'push', remoteMessage: { notification: { title: 'Test' } } })) + mocks.receive(notification({ channelId: 'orca-desktop' })) + mocks.receive(notification(pushTrigger({ ...REMOTE_NOTIFICATION, title: 'Test' }))) expect(mocks.schedule).not.toHaveBeenCalled() }) it('leaves silent dismissals and unrelated messages alone', () => { startAndroidForegroundPushPresentation() - const incoming = notification() - incoming.request.content.data.kind = 'dismiss' - mocks.receive(incoming) - incoming.request.content.data = {} - mocks.receive(incoming) + mocks.receive(notification(undefined, { ...ORCA_PUSH_DATA, kind: 'dismiss' })) + mocks.receive(notification(undefined, {})) expect(mocks.schedule).not.toHaveBeenCalled() }) diff --git a/mobile/src/notifications/notification-reply-schema.test.ts b/mobile/src/notifications/notification-reply-schema.test.ts index fe298b279ff..1e6ae74ff09 100644 --- a/mobile/src/notifications/notification-reply-schema.test.ts +++ b/mobile/src/notifications/notification-reply-schema.test.ts @@ -3,6 +3,8 @@ import type { z } from 'zod' import { missedNotificationsSchema, notificationUnreadReplySchema, + PUSH_REGISTER_REFUSAL_REASONS, + PUSH_TEST_REFUSAL_REASONS, pushDeliveryTestResultSchema, pushRouteRegistrationSchema } from './notification-reply-schema' @@ -73,6 +75,17 @@ describe('closed enums degrade to the copy main showed', () => { reads(pushRouteRegistrationSchema, { registered: false, reason: 'moon-phase' })?.reason ).toBe(undefined) }) + + // Both arm lists are pinned to the host's refusal unions in the schema module, where tsc looks; + // these loops prove every pinned arm survives the parse, not just the ones picked above. + it('keeps every refusal reason the host declares for either route', () => { + for (const reason of PUSH_TEST_REFUSAL_REASONS) { + expect(reads(pushDeliveryTestResultSchema, { accepted: false, reason })?.reason).toBe(reason) + } + for (const reason of PUSH_REGISTER_REFUSAL_REASONS) { + expect(reads(pushRouteRegistrationSchema, { registered: false, reason })?.reason).toBe(reason) + } + }) }) describe('dismissal rows stay opaque', () => { diff --git a/mobile/src/notifications/notification-reply-schema.ts b/mobile/src/notifications/notification-reply-schema.ts index 2432c727487..6cb3ef3e510 100644 --- a/mobile/src/notifications/notification-reply-schema.ts +++ b/mobile/src/notifications/notification-reply-schema.ts @@ -1,5 +1,9 @@ import { z } from 'zod' -import { salvagedOptional } from '../../../src/shared/zod-salvage' +import type { + MobilePushRegisterResult, + MobilePushTestResult +} from '../../../src/shared/mobile-push-contract' +import { hostUnionArms, salvagedOptional } from '../../../src/shared/zod-salvage' // The four notification replies mobile reads: the push-route register/unregister pair, the // settings screen's delivery probe, and the tray catch-up. Checked against the handlers in @@ -20,6 +24,26 @@ import { salvagedOptional } from '../../../src/shared/zod-salvage' */ export const notificationUnreadReplySchema = z.unknown() +// Both reason vocabularies are pinned to the host's own refusal arms through hostUnionArms, so an +// arm added or dropped host-side fails tsc here instead of degrading silently on the phone. +export const PUSH_TEST_REFUSAL_REASONS = hostUnionArms< + Extract['reason'] +>({ + not_registered: true, + unavailable: true, + rate_limited: true, + rejected: true +}) +export const PUSH_REGISTER_REFUSAL_REASONS = hostUnionArms< + Extract['reason'] +>({ + gateway_unreachable: true, + gateway_rejected: true, + not_mobile: true, + registration_storage_failed: true, + throttled: true +}) + /** * Whether Orca's push service took a test notification. * @@ -28,7 +52,7 @@ export const notificationUnreadReplySchema = z.unknown() * `reason` is a closed enum because those two comparisons are the whole of what it decides — an arm * this build does not know degrades to the generic "Could not send" copy, which is the arm main took * for every unrecognised string too — pinned by the `notifications-display-test-unknown-reason` - * golden. The arms are the host's own (mobile-push-contract.ts:99). + * golden. The arms are the host's own (mobile-push-contract.ts:99), pinned to it above. * * Total, so a result that is not an object degrades the same way. A refusal here would not be * silent: the call site's `try` turns it into the reader's own sentence in the message slot where @@ -38,10 +62,7 @@ export const notificationUnreadReplySchema = z.unknown() export const pushDeliveryTestResultSchema = z .looseObject({ accepted: salvagedOptional('accepted', z.boolean()), - reason: salvagedOptional( - 'reason', - z.enum(['not_registered', 'unavailable', 'rate_limited', 'rejected']) - ) + reason: salvagedOptional('reason', z.enum(PUSH_TEST_REFUSAL_REASONS)) }) .nullish() .catch(undefined) @@ -52,23 +73,14 @@ export const pushDeliveryTestResultSchema = z * `registered` is the only member read — push-registration.ts:117 compares it to `true` through * `?.` on a payload main already typed as nullable — so the reply stays nullish and every member * optional. `registrationId` and `reason` are declared because the host sends them - * (MobilePushRegisterResult, mobile-push-contract.ts:38-49, whose five reason arms these are) and a - * future reader should find them here rather than re-assert them. + * (MobilePushRegisterResult, mobile-push-contract.ts:38-49, whose five reason arms these are, + * pinned to it above) and a future reader should find them here rather than re-assert them. */ export const pushRouteRegistrationSchema = z .looseObject({ registered: salvagedOptional('registered', z.boolean()), registrationId: salvagedOptional('registrationId', z.string()), - reason: salvagedOptional( - 'reason', - z.enum([ - 'gateway_unreachable', - 'gateway_rejected', - 'not_mobile', - 'registration_storage_failed', - 'throttled' - ]) - ) + reason: salvagedOptional('reason', z.enum(PUSH_REGISTER_REFUSAL_REASONS)) }) .nullish() diff --git a/mobile/src/session/MobileNativeChatView.test.ts b/mobile/src/session/MobileNativeChatView.test.ts index a245a1fe7bb..6b80c3500ae 100644 --- a/mobile/src/session/MobileNativeChatView.test.ts +++ b/mobile/src/session/MobileNativeChatView.test.ts @@ -262,7 +262,9 @@ describe('MobileNativeChatView', () => { try { const folded = [assistantTurn('a1', 'Starting')] await render({ folded }) - await act(async () => vi.runOnlyPendingTimers()) + await act(async () => { + vi.runOnlyPendingTimers() + }) scrollToEnd.mockClear() await update({ folded, streaming: 'Streaming output' }) @@ -270,7 +272,9 @@ describe('MobileNativeChatView', () => { expect(scrollToOffset).toHaveBeenCalledOnce() expect(scrollToOffset).toHaveBeenLastCalledWith({ animated: false, offset: 900 }) - await act(async () => vi.advanceTimersByTime(60)) + await act(async () => { + vi.advanceTimersByTime(60) + }) expect(scrollToOffset).toHaveBeenCalledOnce() } finally { vi.useRealTimers() @@ -353,7 +357,9 @@ describe('MobileNativeChatView', () => { list().props.onContentSizeChange(320, 1_250) list().props.onMomentumScrollBegin?.({}) }) - await act(async () => vi.advanceTimersByTime(200)) + await act(async () => { + vi.advanceTimersByTime(200) + }) act(() => list().props.onContentSizeChange(320, 1_300)) expect(scrollToEnd).not.toHaveBeenCalled() @@ -439,7 +445,9 @@ describe('MobileNativeChatView', () => { } }) }) - await act(async () => vi.advanceTimersByTime(200)) + await act(async () => { + vi.advanceTimersByTime(200) + }) act(() => list().props.onContentSizeChange(320, 1_250)) expect(scrollToEnd).toHaveBeenCalledOnce() @@ -471,7 +479,9 @@ describe('MobileNativeChatView', () => { expect(scrollToEnd).not.toHaveBeenCalled() expect(scrollToOffset).not.toHaveBeenCalled() - await act(async () => vi.runOnlyPendingTimers()) + await act(async () => { + vi.runOnlyPendingTimers() + }) expect(scrollToEnd).toHaveBeenCalledOnce() expect(scrollToEnd).toHaveBeenLastCalledWith({ animated: false }) @@ -521,7 +531,9 @@ describe('MobileNativeChatView', () => { } }) }) - await act(async () => vi.advanceTimersByTime(200)) + await act(async () => { + vi.advanceTimersByTime(200) + }) act(() => list().props.onContentSizeChange(320, 950)) expect(onLoadEarlier).toHaveBeenCalledOnce() @@ -540,7 +552,9 @@ describe('MobileNativeChatView', () => { try { const folded = [assistantTurn('a1', 'History')] await render({ folded }) - await act(async () => vi.runOnlyPendingTimers()) + await act(async () => { + vi.runOnlyPendingTimers() + }) await scrollAwayFromTail() scrollToEnd.mockClear() @@ -548,7 +562,9 @@ describe('MobileNativeChatView', () => { expect(scrollToEnd).toHaveBeenCalledOnce() expect(scrollToEnd).toHaveBeenLastCalledWith({ animated: false }) - await act(async () => vi.advanceTimersByTime(60)) + await act(async () => { + vi.advanceTimersByTime(60) + }) expect(scrollToEnd).toHaveBeenCalledOnce() } finally { vi.useRealTimers() @@ -577,7 +593,9 @@ describe('MobileNativeChatView', () => { try { const folded = [assistantTurn('a1', 'Latest')] await render({ folded }) - await act(async () => vi.runOnlyPendingTimers()) + await act(async () => { + vi.runOnlyPendingTimers() + }) scrollToEnd.mockClear() await update({ folded, keyboardInset: 320 }) @@ -585,7 +603,9 @@ describe('MobileNativeChatView', () => { expect(scrollToEnd).toHaveBeenCalledOnce() expect(scrollToEnd).toHaveBeenLastCalledWith({ animated: false }) - await act(async () => vi.advanceTimersByTime(60)) + await act(async () => { + vi.advanceTimersByTime(60) + }) expect(scrollToEnd).toHaveBeenCalledOnce() await scrollAwayFromTail() @@ -611,14 +631,20 @@ describe('MobileNativeChatView', () => { vi.useFakeTimers() try { await render({ inputLockReason: 'waiting' }) - await act(async () => vi.advanceTimersByTime(600)) + await act(async () => { + vi.advanceTimersByTime(600) + }) expect(composer().props.disabled).toBe(true) await update({ inputLockReason: null }) expect(composer().props.disabled).toBe(true) - await act(async () => vi.advanceTimersByTime(300)) + await act(async () => { + vi.advanceTimersByTime(300) + }) await update({ inputLockReason: 'waiting' }) - await act(async () => vi.advanceTimersByTime(600)) + await act(async () => { + vi.advanceTimersByTime(600) + }) expect(composer().props.disabled).toBe(true) expect(composer().props.placeholder).toBe('Waiting for terminal…') @@ -631,12 +657,18 @@ describe('MobileNativeChatView', () => { vi.useFakeTimers() try { await render({ inputLockReason: 'waiting' }) - await act(async () => vi.advanceTimersByTime(600)) + await act(async () => { + vi.advanceTimersByTime(600) + }) await update({ inputLockReason: null }) - await act(async () => vi.advanceTimersByTime(599)) + await act(async () => { + vi.advanceTimersByTime(599) + }) expect(composer().props.disabled).toBe(true) - await act(async () => vi.advanceTimersByTime(1)) + await act(async () => { + vi.advanceTimersByTime(1) + }) expect(composer().props.disabled).toBe(false) expect(composer().props.placeholder).toBe('Message, @files, /commands') diff --git a/mobile/src/session/mobile-structured-grouped-question.test.ts b/mobile/src/session/mobile-structured-grouped-question.test.ts index f45c922c6cb..45a365864d9 100644 --- a/mobile/src/session/mobile-structured-grouped-question.test.ts +++ b/mobile/src/session/mobile-structured-grouped-question.test.ts @@ -74,9 +74,12 @@ describe('mobile structured grouped questions', () => { kind: 'advance', draft: { promptKey: PROMPT_KEY, answers: [{ questionId: 'q1', optionIds: ['q1:choice-1'] }] } }) + // Read it non-optionally: an absent advance must fail here, not fall to the null draft and + // leave the assertion below describing the first step again. + expect(advance).toBeDefined() const second = projectGroupedQuestion( questions, - advance!.kind === 'advance' ? advance.draft : null, + advance!.kind === 'advance' ? advance!.draft : null, PROMPT_KEY ) expect(second).toMatchObject({ question: 'Which regions? (2 of 2)', multiSelect: true }) diff --git a/mobile/src/session/mobile-terminal-records.test.ts b/mobile/src/session/mobile-terminal-records.test.ts index bcc9510d0e8..efd368411f4 100644 --- a/mobile/src/session/mobile-terminal-records.test.ts +++ b/mobile/src/session/mobile-terminal-records.test.ts @@ -172,7 +172,7 @@ describe('mobile terminal records', () => { ...base, agentStatus: { ...base.agentStatus!, - state: 'blocked', + state: 'blocked' as const, updatedAt: 2, stateStartedAt: 2 } diff --git a/mobile/src/session/use-mobile-native-chat-answer-send.test.ts b/mobile/src/session/use-mobile-native-chat-answer-send.test.ts index cfb35417e9f..394c5555e2f 100644 --- a/mobile/src/session/use-mobile-native-chat-answer-send.test.ts +++ b/mobile/src/session/use-mobile-native-chat-answer-send.test.ts @@ -138,11 +138,17 @@ describe('useMobileNativeChatAnswerSend', () => { expect(sendRequest).toHaveBeenCalledTimes(1) expect(sendRequest.mock.calls[0]?.[1]).toMatchObject({ text: '1', enter: false }) - await act(async () => vi.advanceTimersByTimeAsync(MOBILE_NATIVE_CHAT_QUESTION_STEP_MS)) + await act(async () => { + await vi.advanceTimersByTimeAsync(MOBILE_NATIVE_CHAT_QUESTION_STEP_MS) + }) expect(sendRequest.mock.calls[1]?.[1]).toMatchObject({ text: '3', enter: false }) - await act(async () => vi.advanceTimersByTimeAsync(MOBILE_NATIVE_CHAT_QUESTION_STEP_MS)) + await act(async () => { + await vi.advanceTimersByTimeAsync(MOBILE_NATIVE_CHAT_QUESTION_STEP_MS) + }) expect(sendRequest.mock.calls[2]?.[1]).toMatchObject({ text: '\x1b[C', enter: false }) - await act(async () => vi.advanceTimersByTimeAsync(MOBILE_NATIVE_CHAT_QUESTION_STEP_MS)) + await act(async () => { + await vi.advanceTimersByTimeAsync(MOBILE_NATIVE_CHAT_QUESTION_STEP_MS) + }) await expect(result).resolves.toBe(true) expect(sendRequest.mock.calls[3]?.[1]).toMatchObject({ text: '\r', enter: false }) }) @@ -161,7 +167,9 @@ describe('useMobileNativeChatAnswerSend', () => { await act(async () => { result = answerSend?.answerAsk(prompt, [{ indices: [1] }, { indices: [0] }]) }) - await act(async () => vi.runAllTimersAsync()) + await act(async () => { + await vi.runAllTimersAsync() + }) await expect(result).resolves.toBe(true) expect(sendRequest.mock.calls.map((call) => call[1])).toEqual([ @@ -191,8 +199,12 @@ describe('useMobileNativeChatAnswerSend', () => { await act(async () => { result = answerSend?.answerAsk(prompt, [{ indices: [1] }, { indices: [0] }]) }) - await act(async () => vi.advanceTimersByTimeAsync(MOBILE_NATIVE_CHAT_QUESTION_STEP_MS)) - await act(async () => vi.advanceTimersByTimeAsync(MOBILE_NATIVE_CHAT_QUESTION_STEP_MS)) + await act(async () => { + await vi.advanceTimersByTimeAsync(MOBILE_NATIVE_CHAT_QUESTION_STEP_MS) + }) + await act(async () => { + await vi.advanceTimersByTimeAsync(MOBILE_NATIVE_CHAT_QUESTION_STEP_MS) + }) await expect(result).resolves.toBe(true) // 15s total transport, minus 6s per completed write; the 1s pacing steps are @@ -209,7 +221,9 @@ describe('useMobileNativeChatAnswerSend', () => { // A newline in raw keystrokes would submit early — must collapse to space. result = answerSend?.answerAsk(TABS_OR_SPACES, [{ indices: [], other: 'zeta\nspaces' }]) }) - await act(async () => vi.runAllTimersAsync()) + await act(async () => { + await vi.runAllTimersAsync() + }) await expect(result).resolves.toBe(true) expect(sendRequest.mock.calls.map((call) => call[1])).toEqual([ @@ -252,7 +266,9 @@ describe('useMobileNativeChatAnswerSend', () => { await act(async () => { result = answerSend?.answerAsk(prompt, [{ indices: [1] }, { indices: [0] }]) }) - await act(async () => vi.runAllTimersAsync()) + await act(async () => { + await vi.runAllTimersAsync() + }) await expect(result).resolves.toBe(true) expect(sendRequest.mock.calls.map((call) => call[1])).toEqual([ @@ -354,7 +370,9 @@ describe('useMobileNativeChatAnswerSend', () => { await act(async () => { result = answerSend?.answerAsk(prompt, [{ indices: [1] }, { indices: [0] }]) }) - await act(async () => vi.runAllTimersAsync()) + await act(async () => { + await vi.runAllTimersAsync() + }) await expect(result).resolves.toBe(false) // The first group DID land, so the remote selector is half-stepped — telling the @@ -398,7 +416,9 @@ describe('useMobileNativeChatAnswerSend', () => { expect(sendRequest).toHaveBeenCalledTimes(1) await setEnabled(false) - await act(async () => vi.runAllTimersAsync()) + await act(async () => { + await vi.runAllTimersAsync() + }) await expect(result).resolves.toBe(false) expect(sendRequest).toHaveBeenCalledTimes(1) @@ -455,7 +475,9 @@ describe('useMobileNativeChatAnswerSend', () => { await act(async () => { second = answerSend?.answerAsk(TABS_OR_SPACES, [{ indices: [1] }]) }) - await act(async () => vi.runAllTimersAsync()) + await act(async () => { + await vi.runAllTimersAsync() + }) await expect(first).resolves.toBe(false) await expect(second).resolves.toBe(false) diff --git a/mobile/src/session/use-mobile-native-chat-controller.test.ts b/mobile/src/session/use-mobile-native-chat-controller.test.ts index 00eee9651a0..0bffa3039f2 100644 --- a/mobile/src/session/use-mobile-native-chat-controller.test.ts +++ b/mobile/src/session/use-mobile-native-chat-controller.test.ts @@ -33,6 +33,7 @@ const structuredOptionSnapshot: SessionOptionDescriptor[] = [ choices: [{ value: 'gpt-fast', label: 'GPT Fast' }] }, valueSource: 'reported', + transport: 'agent-session', settable: true } ] diff --git a/mobile/src/session/use-mobile-native-chat-drafts.test.ts b/mobile/src/session/use-mobile-native-chat-drafts.test.ts index 586b384e770..2547d9191be 100644 --- a/mobile/src/session/use-mobile-native-chat-drafts.test.ts +++ b/mobile/src/session/use-mobile-native-chat-drafts.test.ts @@ -219,7 +219,9 @@ describe('useMobileNativeChatDrafts', () => { // A relay drop can stall the transcript stream past the deadline; the // delivered prompt must not reappear in the composer when it recovers. - act(() => vi.advanceTimersByTime(25_000)) + act(() => { + vi.advanceTimersByTime(25_000) + }) await act(async () => renderer?.update( createElement(Harness, { tabId: 'a', messages: [userTextMessage('m1', 'ping')] }) @@ -510,7 +512,9 @@ describe('useMobileNativeChatDrafts', () => { ) ) - act(() => vi.advanceTimersByTime(30_000)) + act(() => { + vi.advanceTimersByTime(30_000) + }) expect(onUnconfirmed).not.toHaveBeenCalled() } finally { vi.useRealTimers() @@ -557,7 +561,9 @@ describe('useMobileNativeChatDrafts', () => { }) ) ) - act(() => vi.advanceTimersByTime(30_000)) + act(() => { + vi.advanceTimersByTime(30_000) + }) expect(onUnconfirmed).not.toHaveBeenCalled() } finally { vi.useRealTimers() @@ -614,7 +620,9 @@ describe('useMobileNativeChatDrafts', () => { }) expect(vi.getTimerCount()).toBe(0) - act(() => vi.advanceTimersByTime(30_000)) + act(() => { + vi.advanceTimersByTime(30_000) + }) expect(onUnconfirmed).not.toHaveBeenCalled() } finally { vi.useRealTimers() @@ -633,9 +641,13 @@ describe('useMobileNativeChatDrafts', () => { } }) - act(() => vi.advanceTimersByTime(19_999)) + act(() => { + vi.advanceTimersByTime(19_999) + }) expect(onUnconfirmed).not.toHaveBeenCalled() - act(() => vi.advanceTimersByTime(1)) + act(() => { + vi.advanceTimersByTime(1) + }) expect(onUnconfirmed).toHaveBeenCalledTimes(1) } finally { vi.useRealTimers() @@ -670,7 +682,9 @@ describe('useMobileNativeChatDrafts', () => { ) expect(state?.composerText).toBe('ping') - act(() => vi.advanceTimersByTime(30_000)) + act(() => { + vi.advanceTimersByTime(30_000) + }) expect(onUnconfirmed).toHaveBeenCalledTimes(1) } finally { vi.useRealTimers() @@ -704,7 +718,9 @@ describe('useMobileNativeChatDrafts', () => { ) expect(state?.composerText).toBe('ping') - act(() => vi.advanceTimersByTime(30_000)) + act(() => { + vi.advanceTimersByTime(30_000) + }) expect(onUnconfirmed).toHaveBeenCalledTimes(1) } finally { vi.useRealTimers() @@ -731,7 +747,9 @@ describe('useMobileNativeChatDrafts', () => { createElement(Harness, { tabId: 'a', messages: [userTextMessage('echo-1', 'ping')] }) ) ) - act(() => vi.advanceTimersByTime(30_000)) + act(() => { + vi.advanceTimersByTime(30_000) + }) expect(firstUnconfirmed).not.toHaveBeenCalled() expect(secondUnconfirmed).toHaveBeenCalledTimes(1) @@ -757,7 +775,9 @@ describe('useMobileNativeChatDrafts', () => { }) expect(vi.getTimerCount()).toBe(0) - act(() => vi.advanceTimersByTime(30_000)) + act(() => { + vi.advanceTimersByTime(30_000) + }) expect(onUnconfirmed).not.toHaveBeenCalled() } finally { vi.useRealTimers() @@ -807,7 +827,9 @@ describe('useMobileNativeChatDrafts', () => { ) expect(state?.composerText).toBe('ping') - act(() => vi.advanceTimersByTime(30_000)) + act(() => { + vi.advanceTimersByTime(30_000) + }) expect(onUnconfirmed).toHaveBeenCalledTimes(1) } finally { vi.useRealTimers() diff --git a/mobile/src/session/use-mobile-native-chat-file-search.test.ts b/mobile/src/session/use-mobile-native-chat-file-search.test.ts index f815eb88793..725bd591653 100644 --- a/mobile/src/session/use-mobile-native-chat-file-search.test.ts +++ b/mobile/src/session/use-mobile-native-chat-file-search.test.ts @@ -56,10 +56,14 @@ describe('useMobileNativeChatFileSearch', () => { state?.loadNativeChatFiles('a') state?.loadNativeChatFiles('app') }) - await act(async () => vi.advanceTimersByTimeAsync(119)) + await act(async () => { + await vi.advanceTimersByTimeAsync(119) + }) expect(sendRequest).not.toHaveBeenCalled() - await act(async () => vi.advanceTimersByTimeAsync(1)) + await act(async () => { + await vi.advanceTimersByTimeAsync(1) + }) expect(sendRequest).toHaveBeenCalledTimes(1) expect(sendRequest).toHaveBeenCalledWith('files.searchPaths', { worktree: 'id:wt-1', @@ -84,11 +88,15 @@ describe('useMobileNativeChatFileSearch', () => { await mount(fakeClient({ sendRequest })) act(() => state?.loadNativeChatFiles('apple')) - await act(async () => vi.advanceTimersByTimeAsync(120)) + await act(async () => { + await vi.advanceTimersByTimeAsync(120) + }) expect(state?.nativeChatFilePaths).toEqual(['src/apple.ts']) act(() => state?.loadNativeChatFiles('readme')) - await act(async () => vi.advanceTimersByTimeAsync(120)) + await act(async () => { + await vi.advanceTimersByTimeAsync(120) + }) expect(state?.nativeChatFilePaths).toEqual(['docs/readme.md']) expect(sendRequest.mock.calls.map(([method]) => method)).toEqual([ 'files.searchPaths', @@ -104,7 +112,9 @@ describe('useMobileNativeChatFileSearch', () => { // Populate the cache for 'app'. act(() => state?.loadNativeChatFiles('app')) - await act(async () => vi.advanceTimersByTimeAsync(120)) + await act(async () => { + await vi.advanceTimersByTimeAsync(120) + }) expect(state?.nativeChatFilePaths).toEqual(['src/app.ts']) // Schedule 'beta' (debounced, unresolved), then hit the cache for 'app'. @@ -115,7 +125,9 @@ describe('useMobileNativeChatFileSearch', () => { expect(state?.nativeChatFilePaths).toEqual(['src/app.ts']) // The cancelled 'beta' request must never fire and overwrite the cached result. - await act(async () => vi.advanceTimersByTimeAsync(120)) + await act(async () => { + await vi.advanceTimersByTimeAsync(120) + }) expect(state?.nativeChatFilePaths).toEqual(['src/app.ts']) expect( sendRequest.mock.calls.filter(([, params]) => (params as { query: string }).query === 'beta') @@ -141,13 +153,17 @@ describe('useMobileNativeChatFileSearch', () => { const listCalls = (): number => sendRequest.mock.calls.filter(([method]) => method === 'files.list').length act(() => state?.loadNativeChatFiles('apple')) - await act(async () => vi.advanceTimersByTimeAsync(120)) + await act(async () => { + await vi.advanceTimersByTimeAsync(120) + }) expect(state?.nativeChatFilePaths).toEqual(['src/apple.ts']) expect(listCalls()).toBe(1) // Control: a fresh query under the same epoch is answered from the inventory already held. act(() => state?.loadNativeChatFiles('readme')) - await act(async () => vi.advanceTimersByTimeAsync(120)) + await act(async () => { + await vi.advanceTimersByTimeAsync(120) + }) expect(listCalls()).toBe(1) // `migrateTo` advanced the logical authority epoch. The client is the same object and the @@ -155,7 +171,9 @@ describe('useMobileNativeChatFileSearch', () => { // inventory the host under the old authority gave us. generation = 2 act(() => state?.loadNativeChatFiles('guide')) - await act(async () => vi.advanceTimersByTimeAsync(120)) + await act(async () => { + await vi.advanceTimersByTimeAsync(120) + }) expect(listCalls()).toBe(2) expect(state?.nativeChatFilePaths).toEqual(['docs/guide.md']) }) @@ -179,9 +197,13 @@ describe('useMobileNativeChatFileSearch', () => { await mount(fakeClient({ sendRequest })) act(() => state?.loadNativeChatFiles('apple')) - await act(async () => vi.advanceTimersByTimeAsync(120)) + await act(async () => { + await vi.advanceTimersByTimeAsync(120) + }) act(() => state?.loadNativeChatFiles('readme')) - await act(async () => vi.advanceTimersByTimeAsync(120)) + await act(async () => { + await vi.advanceTimersByTimeAsync(120) + }) expect(sendRequest.mock.calls.filter(([method]) => method === 'files.list')).toHaveLength(1) await act(async () => { diff --git a/mobile/src/session/use-mobile-native-chat-input-lease.test.ts b/mobile/src/session/use-mobile-native-chat-input-lease.test.ts index 6e4d12a2bcc..792e2b66d38 100644 --- a/mobile/src/session/use-mobile-native-chat-input-lease.test.ts +++ b/mobile/src/session/use-mobile-native-chat-input-lease.test.ts @@ -33,7 +33,9 @@ describe('useMobileNativeChatInputLease', () => { expect(lease?.ready).toBe(true) expect(lease?.lockReason).toBeNull() - act(() => lease?.clear()) + act(() => { + lease?.clear() + }) expect(lease?.ready).toBe(false) act(() => lease?.markReady('terminal')) expect(lease?.ready).toBe(true) @@ -86,18 +88,26 @@ describe('useSettledMobileNativeChatInputLock', () => { renderer = create(createElement(Harness, { reason: 'waiting' })) }) expect(settled).toBeNull() - act(() => vi.advanceTimersByTime(600)) + act(() => { + vi.advanceTimersByTime(600) + }) expect(settled).toBe('waiting') // A brief unlock that reverts inside the settle window never reaches the composer. act(() => renderer?.update(createElement(Harness, { reason: null }))) - act(() => vi.advanceTimersByTime(300)) + act(() => { + vi.advanceTimersByTime(300) + }) act(() => renderer?.update(createElement(Harness, { reason: 'disconnected' }))) - act(() => vi.advanceTimersByTime(600)) + act(() => { + vi.advanceTimersByTime(600) + }) expect(settled).toBe('disconnected') act(() => renderer?.update(createElement(Harness, { reason: null }))) - act(() => vi.advanceTimersByTime(600)) + act(() => { + vi.advanceTimersByTime(600) + }) expect(settled).toBeNull() }) }) diff --git a/mobile/src/session/use-mobile-native-chat-stop.test.ts b/mobile/src/session/use-mobile-native-chat-stop.test.ts index a1ee9ab4dd9..fa6a69749da 100644 --- a/mobile/src/session/use-mobile-native-chat-stop.test.ts +++ b/mobile/src/session/use-mobile-native-chat-stop.test.ts @@ -75,7 +75,9 @@ describe('useMobileNativeChatStop', () => { expect(sendRequest).toHaveBeenCalledTimes(1) await render(enabled as boolean, streamIdentity as string) - await act(async () => vi.runAllTimersAsync()) + await act(async () => { + await vi.runAllTimersAsync() + }) expect(sendRequest).toHaveBeenCalledTimes(1) }) @@ -102,7 +104,9 @@ describe('useMobileNativeChatStop', () => { await render(true, 'stream-1') act(() => stop?.()) - await act(async () => vi.runAllTimersAsync()) + await act(async () => { + await vi.runAllTimersAsync() + }) expect(onSendError).toHaveBeenCalledOnce() expect(onSendError).toHaveBeenCalledWith('Stop not sent') @@ -183,7 +187,9 @@ describe('useMobileNativeChatStop', () => { act(() => stop?.()) act(() => stop?.()) - await act(async () => vi.runAllTimersAsync()) + await act(async () => { + await vi.runAllTimersAsync() + }) await act(async () => { rejectFirst(new Error('late failure')) await Promise.resolve() @@ -196,7 +202,9 @@ describe('useMobileNativeChatStop', () => { await render(true, 'stream-1') act(() => stop?.()) - await act(async () => vi.runAllTimersAsync()) + await act(async () => { + await vi.runAllTimersAsync() + }) expect(reportWorkerTerminalUserInput).toHaveBeenCalledWith( expect.objectContaining({ sendRequest }), @@ -209,7 +217,9 @@ describe('useMobileNativeChatStop', () => { await render(true, 'stream-1') act(() => stop?.()) - await act(async () => vi.runAllTimersAsync()) + await act(async () => { + await vi.runAllTimersAsync() + }) expect(reportWorkerTerminalUserInput).not.toHaveBeenCalled() }) diff --git a/mobile/src/session/use-throttled-latest-value.test.ts b/mobile/src/session/use-throttled-latest-value.test.ts index 29e58c8fa35..b439f394d77 100644 --- a/mobile/src/session/use-throttled-latest-value.test.ts +++ b/mobile/src/session/use-throttled-latest-value.test.ts @@ -43,7 +43,9 @@ describe('useThrottledLatestValue', () => { update('ab') update('abc') expect(latest).toBe('a') - act(() => vi.advanceTimersByTime(50)) + act(() => { + vi.advanceTimersByTime(50) + }) expect(latest).toBe('abc') }) @@ -53,7 +55,9 @@ describe('useThrottledLatestValue', () => { expect(latest).toBe('a') update(undefined) expect(latest).toBeUndefined() - act(() => vi.advanceTimersByTime(50)) + act(() => { + vi.advanceTimersByTime(50) + }) expect(latest).toBeUndefined() }) }) diff --git a/mobile/src/source-control/mobile-pr-chip-summary.test.ts b/mobile/src/source-control/mobile-pr-chip-summary.test.ts index 36f04eb832c..70d05d669e0 100644 --- a/mobile/src/source-control/mobile-pr-chip-summary.test.ts +++ b/mobile/src/source-control/mobile-pr-chip-summary.test.ts @@ -26,7 +26,7 @@ function check( } function ready(prInfo: PRInfo, checks: PRCheckDetail[]): PrSidebarState { - return { kind: 'ready', data: { pr: prInfo, checks, details: null } } + return { kind: 'ready', data: { pr: prInfo, checks, details: null, checksError: null } } } describe('buildMobilePrChipSummary', () => { diff --git a/mobile/src/source-control/mobile-source-control-primary-action.test.ts b/mobile/src/source-control/mobile-source-control-primary-action.test.ts index 8314ecb2d80..e62ab093eb2 100644 --- a/mobile/src/source-control/mobile-source-control-primary-action.test.ts +++ b/mobile/src/source-control/mobile-source-control-primary-action.test.ts @@ -108,6 +108,7 @@ describe('buildMobileSourceControlPrimaryAction', () => { entries: [], summary: { status: 'ready', + errorMessage: undefined, baseRef: 'main', baseOid: 'base', compareRef: 'HEAD', diff --git a/mobile/src/tasks/task-project-board-reply-schema.test.ts b/mobile/src/tasks/task-project-board-reply-schema.test.ts index 841dcb9d741..c36cc9da6f1 100644 --- a/mobile/src/tasks/task-project-board-reply-schema.test.ts +++ b/mobile/src/tasks/task-project-board-reply-schema.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import type { GitHubProjectOwnerType } from '../../../src/shared/github/project-types' import { + PROJECT_OWNER_TYPE, taskProjectAccessibleListSchema, taskProjectAssignableUserListSchema, taskProjectCommentMutationSchema, @@ -61,13 +61,9 @@ describe('project envelopes', () => { describe('ownerType is a closed enum', () => { it('takes both arms the host validates', () => { - // Keyed by the host's own type, so an arm added to GitHubProjectOwnerType fails tsc here - // rather than silently dropping every row that carries it. - const HOST_OWNER_TYPES: Record = { - organization: true, - user: true - } - for (const ownerType of Object.keys(HOST_OWNER_TYPES)) { + // The arm list is pinned to GitHubProjectOwnerType in the schema module, where tsc looks; this + // loop proves every pinned arm parses rather than dropping the row that carries it. + for (const ownerType of PROJECT_OWNER_TYPE) { const parsed = taskProjectRefSchema.safeParse({ ok: true, owner: 'o', ownerType, number: 3 }) expect(parsed.success && parsed.data).toMatchObject({ ownerType }) } diff --git a/mobile/src/tasks/task-project-board-reply-schema.ts b/mobile/src/tasks/task-project-board-reply-schema.ts index 7ae763440ab..e308325f61f 100644 --- a/mobile/src/tasks/task-project-board-reply-schema.ts +++ b/mobile/src/tasks/task-project-board-reply-schema.ts @@ -1,5 +1,6 @@ import { z } from 'zod' -import { salvagedOptional, salvagingArray } from '../../../src/shared/zod-salvage' +import type { GitHubProjectOwnerType } from '../../../src/shared/github/project-types' +import { hostUnionArms, salvagedOptional, salvagingArray } from '../../../src/shared/zod-salvage' import { assignableUserListSchema, detailCheckListSchema, @@ -42,7 +43,11 @@ import { * The corpus exercises `'organization'` (`tk-project-board-load`); `'user'` is unexercised but * inside the set, so no reply can drop on it. */ -const PROJECT_OWNER_TYPE = ['organization', 'user'] as const +// Pinned to the host's own union through hostUnionArms: an arm added or dropped host-side fails tsc. +export const PROJECT_OWNER_TYPE = hostUnionArms({ + organization: true, + user: true +}) const projectMessage = (name: string) => salvagedOptional(name, z.string()) const projectCount = (name: string) => salvagedOptional(name, z.number()) diff --git a/mobile/src/tasks/task-provider-entity-reply-schema.test.ts b/mobile/src/tasks/task-provider-entity-reply-schema.test.ts index d2af64a1a3e..2e864b17372 100644 --- a/mobile/src/tasks/task-provider-entity-reply-schema.test.ts +++ b/mobile/src/tasks/task-provider-entity-reply-schema.test.ts @@ -3,6 +3,7 @@ import type { z } from 'zod' import { assignableUserListSchema, detailCheckListSchema, + DETAIL_FILE_STATUS, detailCommentListSchema, detailFileListSchema, reviewSummaryListSchema, @@ -154,15 +155,8 @@ describe('a check row and a file row', () => { }) it('carries every file status the host will accept back and drops one it would refuse', () => { - for (const status of [ - 'added', - 'modified', - 'removed', - 'renamed', - 'copied', - 'changed', - 'unchanged' - ]) { + // The arm list is pinned to GitHubPRFile['status'] in the schema module, where tsc looks. + for (const status of DETAIL_FILE_STATUS) { expect(reads(detailFileListSchema, [{ path: 'a', status }])[0]?.status).toBe(status) } // Absent is what the call site turns into `'modified'`; the host's own params enum would diff --git a/mobile/src/tasks/task-provider-entity-reply-schema.ts b/mobile/src/tasks/task-provider-entity-reply-schema.ts index 45678712bef..da243ac616c 100644 --- a/mobile/src/tasks/task-provider-entity-reply-schema.ts +++ b/mobile/src/tasks/task-provider-entity-reply-schema.ts @@ -1,5 +1,6 @@ import { z } from 'zod' -import { salvagedOptional, salvagingArray } from '../../../src/shared/zod-salvage' +import type { GitHubPRFile } from '../../../src/shared/github/pull-request-types' +import { hostUnionArms, salvagedOptional, salvagingArray } from '../../../src/shared/zod-salvage' import { prCount, prFlag, prNullableText, prText } from '../session/github-pr-entity-reply-schema' // The entities the tasks screen's provider replies are built out of: the mutation envelope every @@ -22,15 +23,16 @@ import { prCount, prFlag, prNullableText, prText } from '../session/github-pr-en // plain salvaged-member combinators over zod-salvage, and one definition is what keeps "absent // stays absent, malformed reads as absent" identical on both surfaces. -const DETAIL_FILE_STATUS = [ - 'added', - 'modified', - 'removed', - 'renamed', - 'copied', - 'changed', - 'unchanged' -] as const +// Pinned to the host's own union through hostUnionArms: an arm added or dropped host-side fails tsc. +export const DETAIL_FILE_STATUS = hostUnionArms({ + added: true, + modified: true, + removed: true, + renamed: true, + copied: true, + changed: true, + unchanged: true +}) /** * One conversation comment, as every task sheet holds it. diff --git a/mobile/src/tasks/workspace-source-reply-schema.test.ts b/mobile/src/tasks/workspace-source-reply-schema.test.ts index f4c7237d951..30597e8e536 100644 --- a/mobile/src/tasks/workspace-source-reply-schema.test.ts +++ b/mobile/src/tasks/workspace-source-reply-schema.test.ts @@ -7,6 +7,7 @@ import { repoSetupHooksSchema, repoSparsePresetListSchema, repoSparsePresetSaveSchema, + SSH_CONNECTION_STATUS, sshConnectionStateSchema } from './workspace-source-reply-schema' @@ -57,22 +58,10 @@ describe('the SSH connection record', () => { }) describe('status is an open enum that degrades to disconnected', () => { - // Keyed by SshConnectionStatus so the compiler, not this list, decides what "every arm" means: - // an arm added to or removed from the host union fails tsc here before any test runs. That is - // the check the schema's own arm list cannot make about itself. - const HOST_ARMS: Record = { - disconnected: true, - connecting: true, - 'auth-failed': true, - 'deploying-relay': true, - connected: true, - reconnecting: true, - 'reconnection-failed': true, - error: true - } - + // The arm list is pinned to SshConnectionStatus in the schema module, where tsc looks; this loop + // proves every pinned arm survives the parse rather than degrading. it('takes every arm the host declares, so nothing it sends today degrades', () => { - for (const status of Object.keys(HOST_ARMS)) { + for (const status of SSH_CONNECTION_STATUS) { const parsed = sshConnectionStateSchema.safeParse({ state: { ...connected, status } }) expect(parsed.success && parsed.data).toMatchObject({ status }) } diff --git a/mobile/src/tasks/workspace-source-reply-schema.ts b/mobile/src/tasks/workspace-source-reply-schema.ts index 93e80eb0f64..54b0174f65f 100644 --- a/mobile/src/tasks/workspace-source-reply-schema.ts +++ b/mobile/src/tasks/workspace-source-reply-schema.ts @@ -1,5 +1,11 @@ import { z } from 'zod' -import { openEnum, salvagedOptional, salvagingArray } from '../../../src/shared/zod-salvage' +import type { SshConnectionStatus } from '../../../src/shared/ssh-types' +import { + hostUnionArms, + openEnum, + salvagedOptional, + salvagingArray +} from '../../../src/shared/zod-salvage' // The repo and SSH reads the workspace-create drawer runs. Checked against // src/main/runtime/rpc/methods/ssh.ts:30-46 (getPublicSshState, SshConnectionState in @@ -7,16 +13,17 @@ import { openEnum, salvagedOptional, salvagingArray } from '../../../src/shared/ // answer a bare `string[]`), and repo.ts:87-103/:184-192 (the sparse preset envelopes, the ref // search and the orca.yaml hooks). -const SSH_CONNECTION_STATUS = [ - 'disconnected', - 'connecting', - 'auth-failed', - 'deploying-relay', - 'connected', - 'reconnecting', - 'reconnection-failed', - 'error' -] as const +// Pinned to the host's own union through hostUnionArms: an arm added or dropped host-side fails tsc. +export const SSH_CONNECTION_STATUS = hostUnionArms({ + disconnected: true, + connecting: true, + 'auth-failed': true, + 'deploying-relay': true, + connected: true, + reconnecting: true, + 'reconnection-failed': true, + error: true +}) const sourceText = (name: string) => salvagedOptional(name, z.string()) diff --git a/mobile/src/terminal/use-buffered-terminal-drafts.test.tsx b/mobile/src/terminal/use-buffered-terminal-drafts.test.tsx index 02ae3ddd206..f41e71ed3e9 100644 --- a/mobile/src/terminal/use-buffered-terminal-drafts.test.tsx +++ b/mobile/src/terminal/use-buffered-terminal-drafts.test.tsx @@ -118,7 +118,7 @@ describe('useBufferedTerminalDrafts', () => { renderer = create(createElement(Probe, { activeHandle: 'terminal-old' })) }) act(() => hook().setInput('rejected command')) - let send: ReturnType + let send!: ReturnType act(() => { send = hook().beginBufferedTerminalDraftSend('terminal-old', hook().input) hook().reconcileTerminalTabs( diff --git a/mobile/src/test-support/rpc-recording/README.md b/mobile/src/test-support/rpc-recording/README.md index 903946eb468..f20ce18fb54 100644 --- a/mobile/src/test-support/rpc-recording/README.md +++ b/mobile/src/test-support/rpc-recording/README.md @@ -183,7 +183,7 @@ off the context `client-context.tsx` keeps module-private, and each used to carr `exports.recorderHostClientContext = Ctx;`. That string names a local no type checker follows, so five spellings were five independent ways to reach a `ReferenceError` seconds into a recording. `hostClientContextExposure` is the one copy; the trade is that it sits inside `recorderSha256`, so -editing it re-records all 727 goldens rather than the five families. A rename of the local is still +editing it re-records all 778 goldens rather than the five families. A rename of the local is still invisible to `tsc` — nothing short of editing the product module makes a private local checkable — so `adapter-seam.test.ts` asserts the declaration it names exists exactly once, and refuses a sixth inline copy. @@ -378,8 +378,11 @@ families because no reference states are defined for them. ## What this oracle does and does not see -It replays 368 manifest scenarios against frozen goldens and fails on any divergence: 727 goldens -over 888 tests, all inside `pnpm --dir mobile test`. Counts quoted further down are measurements of +It replays 393 manifest scenarios against frozen goldens and fails on any divergence: 778 goldens +over 781 tests, all inside `pnpm --dir mobile test`. Counted with +`python3 -c "import json;print(len(json.load(open('mobile/rpc-foundation/pilot-scenarios.json'))['scenarios']))"`, +`find mobile/rpc-foundation/goldens -type f | wc -l`, and the reported total of +`vitest run src/test-support/rpc-recording/{pilot,family}-recordings.test.ts src/test-support/rpc-recording/derived-goldens.test.ts`. Counts quoted further down are measurements of the change they describe and are not restatements of this one. For a migration it answers one question — does the rewritten call site produce the same sender calls, settlements, state and effects as main did? @@ -610,10 +613,10 @@ the drop happened under, and records a non-empty report as a `reply-salvage` eff operation, the method, the decoded variant, the dropped paths and the count. Nothing in the product tree changes: the report was already being built and thrown away. -No golden carries one. All 19,384 checked reads in the corpus decode their reply whole, on every -reply partition — the matrix varies the envelope a host sends, not the shape of a row inside a -result — so this observation pins the absence rather than a recorded drop. What it buys is the -next tightening: an element or member schema narrowed so a recorded row stops parsing moves the +44 of the 778 goldens carry one, and every other checked read in the corpus decodes its reply +whole (`grep -l reply-salvage mobile/rpc-foundation/goldens/*.json | wc -l`). The matrix varies the +envelope a host sends rather than the shape of a row inside a result, so on most families this +observation pins an absence rather than a recorded drop. What it buys is the next tightening: an element or member schema narrowed so a recorded row stops parsing moves the golden even where nothing downstream reads the row. `salvage-observation.test.ts` is what keeps the observation itself honest, driving a malformed row and a malformed optional through the real `git.status` reply schema, because a refactor that stopped reporting would otherwise leave every diff --git a/mobile/src/transport/mobile-endpoint-supervisor-test-fakes.ts b/mobile/src/transport/mobile-endpoint-supervisor-test-fakes.ts index 80f4438c160..e026ea26889 100644 --- a/mobile/src/transport/mobile-endpoint-supervisor-test-fakes.ts +++ b/mobile/src/transport/mobile-endpoint-supervisor-test-fakes.ts @@ -26,7 +26,8 @@ export class FakeSession implements RpcClient { getState = () => this.state getReconnectAttempt = () => 0 - getLastConnectedAt = () => null + // Nullable: the escalation suites replace this with a real timestamp. + getLastConnectedAt: () => number | null = () => null onStateChange = (listener: (state: ConnectionState) => void) => { this.listeners.add(listener) return () => this.listeners.delete(listener) diff --git a/mobile/src/worktree/workspace-view-settings.test.ts b/mobile/src/worktree/workspace-view-settings.test.ts index 81ea12621c4..4bcdfecf83b 100644 --- a/mobile/src/worktree/workspace-view-settings.test.ts +++ b/mobile/src/worktree/workspace-view-settings.test.ts @@ -15,6 +15,7 @@ const base: MobileViewState = { sortMode: 'recent', hideSleeping: false, hideDefaultBranch: false, + alwaysShowDefaultBranch: false, filterRepoIds: [], collapsedGroups: [], workspaceStatuses: DEFAULT_MOBILE_WORKSPACE_STATUSES diff --git a/mobile/tests-typecheck-baseline.txt b/mobile/tests-typecheck-baseline.txt new file mode 100644 index 00000000000..0e2c3b3c0f7 --- /dev/null +++ b/mobile/tests-typecheck-baseline.txt @@ -0,0 +1,131 @@ +# Test files that do NOT yet typecheck under mobile/tsconfig.test.json. +# This is a RATCHET: the list may only SHRINK. Do NOT add entries to get CI green — +# an unchecked test is one whose type-level pins prove nothing. +# Regenerate/prune: node scripts/check-tests-typecheck-ratchet.mjs --prune +src/accounts-route-reset-credit.test.ts +src/browser/browser-command-reply-schema.test.ts +src/browser/mobile-browser-frameless-stream.test.tsx +src/components/CodexResetCreditAction.test.ts +src/components/HostProtocolGate.test.ts +src/components/MobileHomeQuickActions.test.ts +src/components/MobileHostCard-truthful-status.test.tsx +src/components/MobileHostCard.test.ts +src/components/MobileMarkdown.file-links.test.ts +src/components/MobileRichMarkdownEditor.test.tsx +src/components/NewWorktreeModal.test.tsx +src/components/PickerModal.accessibility.test.ts +src/components/WorktreeAgentList.test.tsx +src/components/agent-monitoring-indicators.test.ts +src/components/bottom-drawer-close-lifecycle.test.ts +src/components/bottom-drawer-window-handback.test.ts +src/components/codex-reset-credit-capability.test.ts +src/components/mobile-agent-icon-gradient.test.ts +src/components/pr-sidebar/PRCommentsSection.test.ts +src/diagnostics/connection-diagnostics-submission.test.ts +src/dictation/mobile-dictation-setup.test.ts +src/files/MobileFileExplorerPanel.test.ts +src/files/MobileFileMarkdownPreview.test.ts +src/hooks/mobile-dictation-desktop-start.test.ts +src/host-edit-route-accessibility.test.ts +src/host-edit-save-flow.test.ts +src/mock-server-key-pair.test.ts +src/notifications/notification-consent-ownership.test.ts +src/notifications/push-dismissal-native-races.test.ts +src/notifications/push-receive.test.ts +src/notifications/push-registration.test.ts +src/notifications/push-token.test.ts +src/onboarding/MobileOnboardingPage.test.ts +src/onboarding/NotificationOnboardingPreview.test.ts +src/onboarding/legacy-notification-opt-in-route.test.ts +src/onboarding/mobile-onboarding-screen.test.ts +src/session/MobileMarkdownReader.test.tsx +src/session/MobileNativeChatComposer.test.ts +src/session/MobileNativeChatMessage.test.ts +src/session/MobileNativeChatOverlay.test.ts +src/session/MobileNativeChatPermission.test.ts +src/session/MobileNativeChatQuestion.test.tsx +src/session/MobileNativeChatSessionOptionPickers.test.ts +src/session/MobileNativeChatSessionOptionRows.test.ts +src/session/MobileNativeChatTurnStatus.test.ts +src/session/MobileNativeChatView.test.ts +src/session/QuickCommandsList.test.ts +src/session/QuickCommandsSheet.test.ts +src/session/ai-vault-resume-launch.test.ts +src/session/mobile-file-tap-open.test.ts +src/session/mobile-native-chat-open-file.test.ts +src/session/mobile-session-route-parity.test.ts +src/session/mobile-session-tab-activation.test.ts +src/session/mobile-worker-takeover-send-sites.test.ts +src/session/pending-terminal-handle-recovery-poll.test.ts +src/session/session-reply-schema.test.ts +src/session/use-mobile-diff-review-send-actions.test.ts +src/session/use-mobile-file-tap-handlers.test.ts +src/session/use-mobile-native-chat-image-attachments.test.ts +src/session/use-mobile-native-chat-turn-disclosure.test.tsx +src/session/use-mobile-pr-branch-context.test.ts +src/session/use-mobile-pr-sidebar-controller.test.ts +src/session/use-mobile-session-terminal-create-actions.test.ts +src/session/use-mobile-structured-agent-session-send.test.tsx +src/session/use-mobile-structured-agent-session.test.tsx +src/session/use-mobile-structured-prompt-responses.test.tsx +src/session/use-mobile-terminal-inventory-recovery.test.ts +src/settings/native-notification-delivery-settings.test.tsx +src/settings/notification-display-test.test.tsx +src/settings/settings-screen-state.test.tsx +src/settings/voice-settings-poller-refresh.test.tsx +src/source-control/MobileGitHistoryList.test.tsx +src/source-control/mobile-branch-compare.test.ts +src/source-control/mobile-create-pr-action.test.ts +src/source-control/mobile-git-status.test.ts +src/source-control/mobile-hosted-review-create-intent-runner.test.ts +src/source-control/use-mobile-hosted-review-eligibility.test.ts +src/tasks/source-workspace-create.test.ts +src/tasks/task-source-search-reply-schema.test.ts +src/terminal/buffered-terminal-draft-restoration.test.ts +src/terminal/mobile-terminal-query-reply.test.ts +src/terminal/terminal-caret-rendering-oracle.test.ts +src/terminal/terminal-live-accessory-raw-send.test.ts +src/terminal/terminal-live-text-commit.test.ts +src/terminal/terminal-webview-engine-error.test.ts +src/terminal/terminal-webview-query-reply-routing.test.ts +src/terminal/terminal-webview-wheel-scroll.test.ts +src/terminal/use-buffered-terminal-drafts.test.tsx +src/terminal/worker-terminal-takeover-report.test.ts +src/test-support/rpc-recording/native-mounting-substitutes.test.ts +src/test-support/rpc-recording/operation-module-loader.test.ts +src/test-support/rpc-recording/screen-native-substitutes.test.ts +src/transport/client-context.test.ts +src/transport/connection-revival-triggers.test.ts +src/transport/host-credential-cleanup.test.ts +src/transport/host-edit-navigation.test.ts +src/transport/host-list-load-sharing.test.ts +src/transport/host-removal-lifecycle.test.ts +src/transport/host-status-gates.test.ts +src/transport/host-store.test.ts +src/transport/mobile-endpoint-supervisor-nudge.test.ts +src/transport/mobile-relay-background-grace.test.ts +src/transport/mobile-relay-background-lifecycle.test.ts +src/transport/mobile-relay-direct-upgrade.test.ts +src/transport/mobile-relay-e2ee-link.test.ts +src/transport/mobile-relay-pairing-recovery.test.ts +src/transport/mobile-relay-pairing-reply.test.ts +src/transport/mobile-relay-physical-client.test.ts +src/transport/mobile-relay-reconnect-controller.test.ts +src/transport/mobile-relay-resume-director.test.ts +src/transport/mobile-relay-rpc-session-liveness.test.ts +src/transport/mobile-relay-rpc-session.test.ts +src/transport/mobile-relay-runtime-failover.test.ts +src/transport/pairing-relay-candidate.test.ts +src/transport/pre-profile-pairing-coordinator.test.ts +src/transport/relay-host-signed-out-verdict.test.ts +src/transport/rpc-client-synthesized-close-diagnostics.test.ts +src/transport/rpc-operation.test.ts +src/transport/rpc-session-liveness-watchdog.test.ts +src/transport/runtime-capability-probe.test.ts +src/transport/settings-host-client-lifecycle.test.ts +src/worktree/agent-row-display.test.ts +src/worktree/agent-row-lineage.test.ts +src/worktree/host-worktree-refresh.test.ts +src/worktree/worktree-host-row-identity.test.ts +src/worktree/worktree-list-completeness.test.ts +src/worktree/worktree-list-snapshot.test.ts diff --git a/mobile/tsconfig.test.json b/mobile/tsconfig.test.json new file mode 100644 index 00000000000..b6bb998837f --- /dev/null +++ b/mobile/tsconfig.test.json @@ -0,0 +1,18 @@ +{ + // The main config excludes test files so Metro never compiles them into the release bundle, and + // vitest transpiles without typechecking — so until this config existed nothing typechecked a + // mobile test, and a type-level pin written in one proved nothing. + "extends": "./tsconfig.json", + "exclude": [ + "node_modules", + // These four are Node-side, not React Native: they import the desktop main process or + // src/shared/child-process, whose modules are written against @types/node. This program's libs + // are React Native's, where `setTimeout` answers a number rather than a NodeJS.Timeout, so + // pulling that graph in reports ~280 errors about the desktop rather than about mobile. Their + // own runtime coverage is vitest, which runs them under Node. + "scripts/rpc-recording-pin-guard.test.ts", + "src/tasks/agent-launch-mobile-replay.test.ts", + "src/tasks/mobile-agent-launch-architecture.test.ts", + "src/transport/mobile-relay-browser-cancel-budget.test.ts" + ] +} diff --git a/src/shared/zod-salvage.ts b/src/shared/zod-salvage.ts index ccbc9503403..2c64c33b770 100644 --- a/src/shared/zod-salvage.ts +++ b/src/shared/zod-salvage.ts @@ -86,7 +86,10 @@ function recordEntryKeys(raw: unknown): string[] | null { * instead of rejecting the reply. A non-string stays fatal, so this widens the vocabulary without * also accepting the wrong type. Not `.catch()`, which would swallow absence too. */ -export function openEnum( +// `readonly string[]` rather than a non-empty tuple so a hostUnionArms list can feed it; z.enum +// takes the same, so the tuple constraint only excluded callers zod itself accepts. `const` keeps a +// bare literal's arms, which the parsed type is built from. +export function openEnum( values: T, fallback: F ): z.ZodType { @@ -96,11 +99,15 @@ export function openEnum` there checks nothing. + * is an excess property. `NoInfer` keeps U from being read off the record, so omitting the type + * argument leaves it at its `never` default and the parameter becomes `never` — the call fails to + * compile rather than pinning the record only to itself. It belongs in the schema module rather + * than its test because the list is what feeds `z.enum`: the coverage record and the arms the + * schema actually accepts are then one object, and a test-side copy could drift from it. */ -export function hostUnionArms(coverage: Readonly>): readonly U[] { +export function hostUnionArms( + coverage: [U] extends [never] ? never : Readonly, true>> +): readonly U[] { // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the mapped parameter type makes every key exactly a U; Object.keys only loses that at the type level. return Object.keys(coverage) as U[] } From ddbad2218be016f3ce9e833fe65170976e55e0dc Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 19:07:11 -0400 Subject: [PATCH 030/168] chore(relay): drop the live-basis partial index that the planner never picks (#21305) Added in #21301 on the reasoning that the composite (active, deadline) index spans all 6.65M rows to find a few hundred live ones. Measured post-merge against production-shaped history, that reasoning does not hold: a basis is inserted active and flipped to 0, so the partial index accumulates one dead entry per deactivation exactly as the composite one does. Scan buffers are identical to composite-only in every state, 11,099 cold, 2,522 warm, 9 after VACUUM, and the planner picks the composite index throughout. The extra index costs ~65 bytes of WAL per basis insert, about 22% more. The relief is the reaper plus vacuum, which #21301 already ships. Removes the statement from SCHEMA, restores the plan test to pinning the composite index by name, and records why a narrower index is not the cure so the next reader does not re-derive it. --- .../src/credential-cleanup-sweep-postgres.test.ts | 9 +++++---- cloud/apps/relay/src/database.ts | 12 +++++------- .../src/postgres-maintenance-sweep-plans.test.ts | 5 +---- .../relay/src/relay-schema-lock-targets.test.ts | 13 ------------- 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/cloud/apps/relay/src/credential-cleanup-sweep-postgres.test.ts b/cloud/apps/relay/src/credential-cleanup-sweep-postgres.test.ts index aa720a67b2d..45f38326d3a 100644 --- a/cloud/apps/relay/src/credential-cleanup-sweep-postgres.test.ts +++ b/cloud/apps/relay/src/credential-cleanup-sweep-postgres.test.ts @@ -120,10 +120,11 @@ describePostgres('credential cleanup against PostgreSQL', () => { expect(expiry).toContain('relay_invites_sweep_expiry') }) - it('plans the live-basis sweep off an index rather than the 1.5 GB heap', async () => { + it('plans the basis sweep off the composite index rather than the 1.5 GB heap', async () => { // The shape that made this the most expensive statement in the sweep: 20,000 settled bases to - // 50 live ones, so the composite (active, deadline) index spans 400x the rows the sweep wants. - // Which index serves it is the planner's call, the same as for the two invite sweeps below. + // 50 live ones. A partial index on active = 1 looks like the answer to that ratio and is not: + // a basis is inserted active and flipped to 0, so it accumulates the same dead entries, and + // the planner picks the composite index anyway. See the schema comment beside it. await database.query( `INSERT INTO relay_connection_bases (basis_conn_id, user_id, relay_host_id, relay_device_id, owning_control_generation, @@ -148,7 +149,7 @@ describePostgres('credential cleanup against PostgreSQL', () => { ) expect(sweep).not.toContain('Seq Scan on relay_connection_bases') - expect(sweep).toMatch(/using relay_connection_bases_(active|live)_deadline/) + expect(sweep).toContain('using relay_connection_bases_active_deadline') }) it('plans the drained basis reaper off the composite index, not the heap', async () => { diff --git a/cloud/apps/relay/src/database.ts b/cloud/apps/relay/src/database.ts index a62bb35798f..972f3ce2aa1 100644 --- a/cloud/apps/relay/src/database.ts +++ b/cloud/apps/relay/src/database.ts @@ -172,16 +172,14 @@ CREATE TABLE IF NOT EXISTS relay_connection_bases ( -- accumulate unboundedly. Unindexed it seq-scans millions of rows every cycle -- and holds the maintenance transaction open long enough to time out -- assignment lock waits. +-- Why not a partial index on active = 1: a basis is inserted active and flipped to 0, so each +-- deactivation leaves a dead entry in that index too. Measured on production-shaped history it +-- carries the same dead entries as this one, the planner picks this one in every state, and it +-- costs ~65 bytes of WAL per insert. Bloat here is cured by reaping and vacuum, not by a narrower +-- index. CREATE INDEX IF NOT EXISTS relay_connection_bases_active_deadline ON relay_connection_bases(active, deadline); --- schema-deferrable: created out of band, so a boot that cannot take the lock must retry --- Why: the index above spans every row, and inactive bases outnumber live ones by ~6.6M to a few --- hundred, so the sweep still walked ~283 MB of index to find them. This one holds only the rows --- the sweep can act on. Keeping both: the composite is also what makes the reaper an index range. -CREATE INDEX IF NOT EXISTS relay_connection_bases_live_deadline - ON relay_connection_bases(deadline) WHERE active = 1; - CREATE TABLE IF NOT EXISTS relay_direct_authorizations ( direct_auth_id TEXT PRIMARY KEY, user_id TEXT NOT NULL, diff --git a/cloud/apps/relay/src/postgres-maintenance-sweep-plans.test.ts b/cloud/apps/relay/src/postgres-maintenance-sweep-plans.test.ts index de8573a0d6c..a8d0e1e3bf9 100644 --- a/cloud/apps/relay/src/postgres-maintenance-sweep-plans.test.ts +++ b/cloud/apps/relay/src/postgres-maintenance-sweep-plans.test.ts @@ -56,9 +56,6 @@ describePostgres('PostgreSQL maintenance sweep plans', () => { const plan = result.rows.map((row) => String(row['QUERY PLAN'])).join('\n') expect(plan).not.toMatch(/Seq Scan on relay_connection_bases/) - // Either index keeps the sweep off the table. It used to be the composite one; the partial - // relay_connection_bases_live_deadline now wins on cost, because it spans only the live rows - // rather than all ~6.6M, and that is the improvement, not a regression in this invariant. - expect(plan).toMatch(/relay_connection_bases_(active|live)_deadline/) + expect(plan).toMatch(/relay_connection_bases_active_deadline/) }) }) diff --git a/cloud/apps/relay/src/relay-schema-lock-targets.test.ts b/cloud/apps/relay/src/relay-schema-lock-targets.test.ts index 26348bb7d9c..68a7c6f0216 100644 --- a/cloud/apps/relay/src/relay-schema-lock-targets.test.ts +++ b/cloud/apps/relay/src/relay-schema-lock-targets.test.ts @@ -27,12 +27,6 @@ const GOLDEN_LOCK_TAKING: SchemaLockTarget[] = [ { kind: 'index', table: 'relay_devices', name: 'relay_devices_current_hash', skipWhen: 'present' }, { kind: 'index', table: 'relay_devices', name: 'relay_devices_grace_hash', skipWhen: 'present' }, { kind: 'index', table: 'relay_connection_bases', name: 'relay_connection_bases_active_deadline', skipWhen: 'present' }, - { - kind: 'index', - table: 'relay_connection_bases', - name: 'relay_connection_bases_live_deadline', - skipWhen: 'present' - }, { kind: 'index', table: 'relay_direct_authorizations', @@ -251,7 +245,6 @@ describe('relay boot-time lock targets', () => { expect(deferrable.map((statement) => sqlWithoutComments(statement).replace(/\s+/g, ' '))).toEqual([ "CREATE INDEX IF NOT EXISTS relay_invites_sweep_expiry ON relay_invites(expires_at) WHERE state IN ('available', 'reserved', 'cooldown')", "CREATE INDEX IF NOT EXISTS relay_invites_sweep_reservation ON relay_invites(reservation_expires_at) WHERE state = 'reserved'", - 'CREATE INDEX IF NOT EXISTS relay_connection_bases_live_deadline ON relay_connection_bases(deadline) WHERE active = 1', 'CREATE INDEX IF NOT EXISTS relay_direct_authorizations_pending_deadline ON relay_direct_authorizations(deadline) WHERE consumed_at IS NULL', 'CREATE INDEX IF NOT EXISTS relay_rate_windows_started ON relay_rate_windows(window_started_at)', 'DROP INDEX IF EXISTS relay_assignment_activity_expiry', @@ -274,12 +267,6 @@ describe('relay boot-time lock targets', () => { name: 'relay_invites_sweep_reservation', skipWhen: 'present' }, - { - kind: 'index', - table: 'relay_connection_bases', - name: 'relay_connection_bases_live_deadline', - skipWhen: 'present' - }, { kind: 'index', table: 'relay_direct_authorizations', From 91ade4b82a64d4200a2830bda67d49ebb2c09a4f Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 19:25:54 -0400 Subject: [PATCH 031/168] perf(relay): batch control lease renewals per cell instead of one write transaction per host (#21303) * perf(relay): batch control lease renewals per cell instead of one write transaction per host Every connected desktop renewed its own control lease with its own single-row write transaction every 30s. At ~14,000 hosts that is ~470 write transactions per second fleet-wide, each with its own transaction id, all updating the same few heap pages of relay_assignments and relay_assignment_activity_leases. Sampling three onsets at 250ms showed no lock queue and no slow statement: 60-144 backends piled into LWLock:BufferContent and Timeout/SpinDelay inside that one statement, and Query Insights attributed 152 of 157 seconds of lightweight-lock wait in the onset minute to it. The heartbeat now enqueues a due renewal instead of issuing it. A cell flushes its queue once per second, or as soon as 500 rows are waiting, through one statement that unnests the parameter arrays and applies the same CTE row-wise. Concurrent writers drop from the host count to the cell count, and transaction ids with them. Measured against a 20,000-row table: 1 row 4.1ms, 12 rows 3.3ms, 100 rows 6.6ms, 500 rows 22.7ms. Per-session semantics are unchanged. Each enqueue still resolves on a renewal and rejects with the outcome as its message, so the completed-attempt counter, the staleness guard, and every close path route exactly as before, and one outcome per row is recorded against the flush latency. Lock order is (user_id, relay_host_id), the primary key of relay_assignments, applied in JavaScript and repeated as the statement's ORDER BY. EXPLAIN confirms LockRows sits above that Sort, so a batch acquires its assignment rows in one global order. Every writer in the store locks a host's assignment row before its migration or lease rows and only ever touches one host, so a batch can only wait on a row a single-host writer holds, never the reverse. One statement also means one contended row could fail the whole batch, so a failed batch degrades to the per-host statements it replaced rather than costing every other host on the cell its renewal. * perf(relay): batch control lease renewals per cell instead of one write transaction per host Every connected desktop renewed its own control lease with its own single-row write transaction every 30s. At ~14,000 hosts that is ~470 write transactions per second fleet-wide, each with its own transaction id, all updating the same few heap pages of relay_assignments and relay_assignment_activity_leases. Sampling three onsets at 250ms showed no lock queue and no slow statement: 60-144 backends piled into LWLock:BufferContent and Timeout/SpinDelay inside that one statement, and Query Insights attributed 152 of 157 seconds of lightweight-lock wait in the onset minute to it. The heartbeat now enqueues a due renewal instead of issuing it. A cell flushes its queue every second, or as soon as 200 rows are waiting, through one statement that unnests the parameter arrays and applies the same CTE row-wise. Concurrent writers drop from the host count to the cell count, and transaction ids with them. Per-host buffer traffic is unchanged: 30 hits for one row, 25.5 per host at 12 rows, 30.1 per host at 200, against the 28.7 the single-row statement reports in production. Per-session semantics are unchanged. Each enqueue still resolves on a renewal and rejects with the outcome as its message, so the completed-attempt counter, the staleness guard, and every close path route as before, and one outcome per row is recorded against the flush latency. Row locks live until the statement commits, so a batch that waited on a contended row would hold every other row's lock for that whole wait. The assignment pass therefore takes its locks with SKIP LOCKED and reports a contended host as assignment_lock_unavailable, which the registry retries on the next tick instead of closing the control. That keeps the hold to the statement's own execution: 11.5ms for 200 rows against a 20,000-row table, and 9.4ms with a host wedged in a per-host transaction, where a blocking FOR UPDATE spends the pool's whole 1s lock_timeout and then fails every row in the flush. An unlocked present_assignment probe separates a host with no assignment row from one the skip passed over, so a skipped row can never be mistaken for a missing assignment and close a live desktop. With no wait on the assignment pass the lock order is only needed for the two later passes, and it holds: every writer takes a host's assignment row before that host's lease rows, and a host whose assignment row is held was skipped, so the batch never reaches its lease. markMigrationTargetRegistered is the one writer that locks a migration row first, and it takes no further locks. * fix(relay): answer every row of a control-renewal batch from its own lease update Review findings on the batched renewal. Two control leases on one host in one batch made the second report control_activity_not_found although both were renewed: the assignment UPDATE is offered the same target row twice, applies one source row and returns one, so the other row_index never came back. The verdict now reads renewed_lease, which has a row per input row, and the assignment UPDATE groups per host so it also stops taking an arbitrary one of the two expiries instead of the later one. The queue now partitions per (userId, relayHostId) rather than per activity, so a second control activity for one host opens the next flush instead of sharing this one. Belt to the statement fix, not a substitute: the store API has to be right for the rows it accepts. renewControlActivities recorded no outcome for a one-row flush that threw, and none at all when every row failed validation, where a mixed batch recorded its invalid_* rows. Both now record in a finally, the way the single-row path's finally always did, and the error-to-outcome mapping both paths share is one function. The four flush fields the runtime metrics event emits had no log-based metric, so add them next to the existing controlRenewalLatencyMs* entries. Applying the Terraform is a separate manual step. controlRenewalLatencyMsP50/P95/Max now measure a batched row's flush duration rather than its own statement latency. Left named as they are for history, with a line at the emit site recording that the meaning changed here. --- cloud/apps/relay/src/assignment-store.ts | 278 ++++++++++------- .../control-lease-recovery-postgres.test.ts | 10 +- .../src/control-renewal-batch-store.test.ts | 291 ++++++++++++++++++ .../relay/src/control-renewal-batch.test.ts | 231 ++++++++++++++ cloud/apps/relay/src/control-renewal-batch.ts | 148 +++++++++ .../src/control-renewal-postgres.test.ts | 169 +++++++++- .../relay/src/control-renewal-statement.ts | 230 ++++++++++++++ .../relay/src/host-session-registry.test.ts | 110 ++++++- cloud/apps/relay/src/host-session-registry.ts | 33 +- cloud/apps/relay/src/relay-observability.ts | 22 ++ cloud/infra/terraform/relay-observability.tf | 4 + 11 files changed, 1392 insertions(+), 134 deletions(-) create mode 100644 cloud/apps/relay/src/control-renewal-batch-store.test.ts create mode 100644 cloud/apps/relay/src/control-renewal-batch.test.ts create mode 100644 cloud/apps/relay/src/control-renewal-batch.ts create mode 100644 cloud/apps/relay/src/control-renewal-statement.ts diff --git a/cloud/apps/relay/src/assignment-store.ts b/cloud/apps/relay/src/assignment-store.ts index bb7812dda86..8c12872effc 100644 --- a/cloud/apps/relay/src/assignment-store.ts +++ b/cloud/apps/relay/src/assignment-store.ts @@ -45,6 +45,15 @@ import { ASSIGNMENT_CONNECTION_HEADROOM_QUERY } from './assignment-connection-headroom-query.js' import { AssignmentIdentityQueue } from './assignment-identity-queue.js' +import { + CONTROL_RENEWAL_BATCH_SQL, + CONTROL_RENEWAL_STATEMENT_OUTCOMES, + controlRenewalBatchParams, + orderedControlRenewalRows, + readControlRenewalOutcomes, + type ControlRenewalOutcome, + type ControlRenewalRequest +} from './control-renewal-statement.js' import { REGIONAL_REHOME_DEFAULT_HOST_COOLDOWN_MS } from './database.js' @@ -101,21 +110,8 @@ type RelayAssignmentStoreOptions = { recordControlRenewal?: (durationMs: number, outcome: ControlRenewalOutcome) => void } -export type ControlRenewalOutcome = - | 'renewed' - | 'assignment_not_found' - | 'activity_cell_not_authoritative' - | 'control_activity_not_found' - | 'control_activity_moved' - | 'database_error' +export type { ControlRenewalOutcome, ControlRenewalRequest } -const CONTROL_RENEWAL_OUTCOMES = new Set([ - 'renewed', - 'assignment_not_found', - 'activity_cell_not_authoritative', - 'control_activity_not_found', - 'control_activity_moved' -]) export type RelayAssignment = AssignmentIdentity & { cellId: string cellUrl: string @@ -3478,132 +3474,149 @@ export class RelayAssignmentStore { }) } + // Kept as the single-row contract for callers and tests: resolves on a + // renewal and throws the outcome (or the driver's own error) otherwise. async renewControlActivity( identity: AssignmentIdentity, input: { activityId: string; cellId: string; expiresAt: number } ): Promise { validateActivityId(input.activityId) const now = this.now() - const maximumExpiresAt = - now + - ASSIGNMENT_LIMITS.activityLeaseMs + - RELAY_PROTOCOL_LIMITS.controlPingIntervalMs * 2 - if ( - !Number.isSafeInteger(input.expiresAt) || - input.expiresAt <= now || - input.expiresAt > maximumExpiresAt - ) { + if (!controlRenewalExpiryIsValid(input.expiresAt, now)) { throw new Error('invalid_activity_expiry') } const startedAt = performance.now() let outcome: ControlRenewalOutcome = 'database_error' try { - outcome = - this.database.dialect === 'postgres' - ? await this.renewPostgresControlActivity(identity, input, now) - : await this.renewTransactionalControlActivity(identity, input, now) + outcome = await this.renewOneControlActivity({ identity, ...input }, now) if (outcome !== 'renewed') throw new Error(outcome) } catch (error) { - const message = String((error as { message?: unknown }).message) - if (CONTROL_RENEWAL_OUTCOMES.has(message as ControlRenewalOutcome)) { - outcome = message as ControlRenewalOutcome - } + outcome = controlRenewalOutcomeOfError(error) throw error } finally { this.recordControlRenewal?.(performance.now() - startedAt, outcome) } } - private async renewPostgresControlActivity( - identity: AssignmentIdentity, - input: { activityId: string; cellId: string; expiresAt: number }, + // Renews every due control lease on a cell in one write transaction, returning + // one outcome per input row in input order. Never throws for a multi-row batch: + // a caller routes its own session on its own outcome. + async renewControlActivities( + rows: readonly ControlRenewalRequest[] + ): Promise { + const now = this.now() + const outcomes = new Array(rows.length) + const accepted: Array = [] + for (const [index, row] of rows.entries()) { + const rejection = controlRenewalRejection(row, now) + if (rejection) outcomes[index] = rejection + else accepted.push({ ...row, index }) + } + const startedAt = performance.now() + try { + if (accepted.length === 0) return outcomes + let results: ControlRenewalOutcome[] + try { + results = await this.executeControlRenewals(accepted, now) + } catch (error) { + if (rows.length === 1) { + outcomes[accepted[0]!.index] = controlRenewalOutcomeOfError(error) + throw error + } + results = accepted.map(() => 'database_error') + } + for (const [position, row] of accepted.entries()) outcomes[row.index] = results[position]! + return outcomes + } finally { + // Every path, so a rethrown lone renewal and an all-invalid batch are + // counted the same as a batch that reached PostgreSQL. + const durationMs = performance.now() - startedAt + for (const outcome of outcomes) this.recordControlRenewal?.(durationMs, outcome) + } + } + + private async executeControlRenewals( + rows: readonly ControlRenewalRequest[], + now: number + ): Promise { + if (rows.length === 1) return [await this.renewOneControlActivity(rows[0]!, now)] + if (this.database.dialect !== 'postgres') { + // Correctness over throughput: the SQLite writer is serialized anyway, and + // this is the dialect the unit suites run on. + return await this.renewControlActivitiesInSeries(rows, now) + } + const ordered = orderedControlRenewalRows(rows.map((row, index) => ({ ...row, index }))) + const outcomes = new Array(rows.length) + try { + const parsed = readControlRenewalOutcomes( + await this.database.query( + CONTROL_RENEWAL_BATCH_SQL, + controlRenewalBatchParams(ordered, now) + ), + ordered.length + ) + for (const [position, row] of ordered.entries()) outcomes[row.index] = parsed[position]! + return outcomes + } catch (error) { + // One statement means one contended assignment row can fail the whole + // batch, so a failure degrades to the per-host statements this replaced + // rather than costing every other host on the cell its renewal. + console.warn( + JSON.stringify({ + event: 'orca_relay_control_renewal_batch_failed', + rows: ordered.length, + message: String((error as { message?: unknown }).message) + }) + ) + await Promise.all( + ordered.map(async (row) => { + try { + outcomes[row.index] = await this.renewOneControlActivity(row, now) + } catch { + outcomes[row.index] = 'database_error' + } + }) + ) + return outcomes + } + } + + private async renewControlActivitiesInSeries( + rows: readonly ControlRenewalRequest[], + now: number + ): Promise { + const outcomes: ControlRenewalOutcome[] = [] + for (const row of rows) { + try { + outcomes.push(await this.renewOneControlActivity(row, now)) + } catch { + outcomes.push('database_error') + } + } + return outcomes + } + + // Returns the outcome; a driver or pool failure reaches the caller unchanged. + private async renewOneControlActivity( + row: ControlRenewalRequest, now: number ): Promise { - const row = ( - await this.database.query( - `WITH assignment_state AS MATERIALIZED ( - SELECT cell_id, assignment_epoch - FROM relay_assignments - WHERE user_id = ? AND relay_host_id = ? - FOR UPDATE - ), migration_state AS MATERIALIZED ( - SELECT migration.assignment_epoch - FROM relay_assignment_migrations migration - JOIN assignment_state assignment - ON migration.target_cell_id = assignment.cell_id - AND migration.assignment_epoch = assignment.assignment_epoch - WHERE migration.user_id = ? AND migration.relay_host_id = ? - AND migration.source_cell_id = ? - AND migration.completed_at IS NULL AND migration.aborted_at IS NULL - FOR UPDATE OF migration - ), authorization_state AS MATERIALIZED ( - SELECT 1 AS authorized - FROM assignment_state assignment - WHERE assignment.cell_id = ? OR EXISTS (SELECT 1 FROM migration_state) - ), lease_state AS MATERIALIZED ( - SELECT lease.activity_kind, lease.cell_id - FROM relay_assignment_activity_leases lease - CROSS JOIN authorization_state - WHERE lease.user_id = ? AND lease.relay_host_id = ? AND lease.activity_id = ? - FOR UPDATE OF lease - ), renewed_lease AS ( - UPDATE relay_assignment_activity_leases lease - SET expires_at = GREATEST(lease.expires_at, ?), - updated_at = GREATEST(lease.updated_at, ?) - FROM lease_state state - WHERE lease.user_id = ? AND lease.relay_host_id = ? AND lease.activity_id = ? - AND state.activity_kind = 'control' AND state.cell_id = ? - RETURNING 1 - ), renewed_assignment AS ( - UPDATE relay_assignments assignment - SET lease_expires_at = GREATEST(assignment.lease_expires_at, ?), - last_activity_at = GREATEST(assignment.last_activity_at, ?) - WHERE assignment.user_id = ? AND assignment.relay_host_id = ? - AND EXISTS (SELECT 1 FROM renewed_lease) - RETURNING 1 - ) - SELECT CASE - WHEN NOT EXISTS (SELECT 1 FROM assignment_state) - THEN 'assignment_not_found' - WHEN NOT EXISTS (SELECT 1 FROM authorization_state) - THEN 'activity_cell_not_authoritative' - WHEN NOT EXISTS (SELECT 1 FROM lease_state) - THEN 'control_activity_not_found' - WHEN EXISTS ( - SELECT 1 FROM lease_state - WHERE activity_kind <> 'control' OR cell_id <> ? - ) THEN 'control_activity_moved' - WHEN EXISTS (SELECT 1 FROM renewed_assignment) THEN 'renewed' - ELSE 'control_activity_not_found' - END AS outcome`, - [ - identity.userId, - identity.relayHostId, - identity.userId, - identity.relayHostId, - input.cellId, - input.cellId, - identity.userId, - identity.relayHostId, - input.activityId, - input.expiresAt, - now, - identity.userId, - identity.relayHostId, - input.activityId, - input.cellId, - input.expiresAt, - now, - identity.userId, - identity.relayHostId, - input.cellId - ] - ) - )[0] - if (!row) throw new Error('missing_control_renewal_outcome') - const outcome = text(row, 'outcome') as ControlRenewalOutcome - if (!CONTROL_RENEWAL_OUTCOMES.has(outcome)) throw new Error('invalid_control_renewal_outcome') - return outcome + if (this.database.dialect === 'postgres') { + return readControlRenewalOutcomes( + await this.database.query( + CONTROL_RENEWAL_BATCH_SQL, + controlRenewalBatchParams([row], now) + ), + 1 + )[0]! + } + try { + return await this.renewTransactionalControlActivity(row.identity, row, now) + } catch (error) { + const message = String((error as { message?: unknown }).message) + if (!CONTROL_RENEWAL_STATEMENT_OUTCOMES.has(message as ControlRenewalOutcome)) throw error + return message as ControlRenewalOutcome + } } private async renewTransactionalControlActivity( @@ -7900,6 +7913,33 @@ function validateActivityId(activityId: string): void { if (!activityId || activityId.length > 256) throw new Error('invalid_activity_id') } +function controlRenewalExpiryIsValid(expiresAt: number, now: number): boolean { + const maximumExpiresAt = + now + ASSIGNMENT_LIMITS.activityLeaseMs + RELAY_PROTOCOL_LIMITS.controlPingIntervalMs * 2 + return Number.isSafeInteger(expiresAt) && expiresAt > now && expiresAt <= maximumExpiresAt +} + +// A renewal that threw still owes the metric an outcome: the message carries one +// when the statement decided it, and anything else is the driver failing. +function controlRenewalOutcomeOfError(error: unknown): ControlRenewalOutcome { + const message = String((error as { message?: unknown }).message) + return CONTROL_RENEWAL_STATEMENT_OUTCOMES.has(message as ControlRenewalOutcome) + ? (message as ControlRenewalOutcome) + : 'database_error' +} + +function controlRenewalRejection( + row: ControlRenewalRequest, + now: number +): ControlRenewalOutcome | null { + try { + validateActivityId(row.activityId) + } catch { + return 'invalid_activity_id' + } + return controlRenewalExpiryIsValid(row.expiresAt, now) ? null : 'invalid_activity_expiry' +} + function activityKind(row: SqlRow): AssignmentActivityKind { const value = text(row, 'activity_kind') if (!(value in ACTIVITY_REQUEST_UNITS)) throw new Error('invalid_activity_kind') diff --git a/cloud/apps/relay/src/control-lease-recovery-postgres.test.ts b/cloud/apps/relay/src/control-lease-recovery-postgres.test.ts index 2fa01c27df8..fed7f56b31b 100644 --- a/cloud/apps/relay/src/control-lease-recovery-postgres.test.ts +++ b/cloud/apps/relay/src/control-lease-recovery-postgres.test.ts @@ -3,6 +3,7 @@ import { ASSIGNMENT_LIMITS, RELAY_CLOSE_CODE } from '@orca-cloud/relay-contract' import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import type WebSocket from 'ws' import { RelayAssignmentStore } from './assignment-store.js' +import { CONTROL_RENEWAL_BATCH_INTERVAL_MS } from './control-renewal-batch.js' import type { RelayConfig } from './config.js' import type { RelayCredentialStore } from './credential-store.js' import { openRelayDatabase, type RelayDatabase } from './database.js' @@ -133,6 +134,10 @@ describePostgres('expired control lease after a database outage', () => { internals.heartbeat(session) } + // A due renewal leaves the heartbeat as a batch enqueue, so a poll has to + // outlast the batch window before it can call the renewal missing. + const renewalPoll = { timeout: CONTROL_RENEWAL_BATCH_INTERVAL_MS + 4_000 } + const leaseRows = async (relayHostId: string) => await database.query( `SELECT activity_id, cell_id FROM relay_assignment_activity_leases @@ -149,7 +154,8 @@ describePostgres('expired control lease after a database outage', () => { .poll( async () => socket.close.mock.calls.length > 0 || - (await leaseRows(relayHostId)).length === expectedRows + (await leaseRows(relayHostId)).length === expectedRows, + renewalPoll ) .toBe(true) } @@ -212,7 +218,7 @@ describePostgres('expired control lease after a database outage', () => { ).rejects.toThrow('control_activity_moved') heartbeat(registry, session) - await expect.poll(() => socket.close.mock.calls.length).toBe(1) + await expect.poll(() => socket.close.mock.calls.length, renewalPoll).toBe(1) expect(socket.close).toHaveBeenCalledWith(RELAY_CLOSE_CODE.DRAINING, 'control activity moved') expect(await leaseRows(identity.relayHostId)).toEqual([ diff --git a/cloud/apps/relay/src/control-renewal-batch-store.test.ts b/cloud/apps/relay/src/control-renewal-batch-store.test.ts new file mode 100644 index 00000000000..e3798a19ae2 --- /dev/null +++ b/cloud/apps/relay/src/control-renewal-batch-store.test.ts @@ -0,0 +1,291 @@ +import { ASSIGNMENT_LIMITS } from '@orca-cloud/relay-contract' +import { describe, expect, it, vi } from 'vitest' +import { RelayAssignmentStore } from './assignment-store.js' +import { CONTROL_RENEWAL_BATCH_SQL } from './control-renewal-statement.js' +import type { ControlRenewalOutcome } from './control-renewal-statement.js' +import { + openInMemoryRelayDatabase, + type RelayDatabase, + type SqlRow +} from './database.js' + +const now = 1_900_000_000_000 +const expiresAt = now + 105_000 + +function renewal(userId: string, relayHostId: string, expiry = expiresAt) { + return { + identity: { userId, relayHostId }, + activityId: 'control:cell-a:1', + cellId: 'cell-a', + expiresAt: expiry + } +} + +// A PostgreSQL-dialect database that answers the renewal statement without a +// server, so the statement count and its parameter arrays are observable. +class RenewalStatementProbe implements RelayDatabase { + readonly dialect = 'postgres' as const + readonly statements: Array<{ sql: string; params: unknown[] }> = [] + failuresRemaining = 0 + + constructor(private readonly outcomeFor: (userId: string) => ControlRenewalOutcome) {} + + async query(sql: string, params: unknown[] = []): Promise { + this.statements.push({ sql, params }) + if (this.failuresRemaining > 0) { + this.failuresRemaining -= 1 + throw new Error('canceling statement due to statement timeout') + } + const userIds = params[0] as string[] + return userIds.map((userId, index) => ({ + row_index: String(index + 1), + outcome: this.outcomeFor(userId) + })) + } + + async queryLocked(): Promise { + throw new Error('unexpected_locked_query') + } + + async transaction(): Promise { + // Renewals must never open one: that is the write transaction per host this + // batch exists to remove. + throw new Error('unexpected_transaction') + } + + async close(): Promise {} +} + +describe('batched control renewals on PostgreSQL', () => { + it('spends one statement on every host that came due', async () => { + const probe = new RenewalStatementProbe(() => 'renewed') + const store = new RelayAssignmentStore(probe, () => now) + + const outcomes = await store.renewControlActivities([ + renewal('user-a', 'host000000000001'), + renewal('user-a', 'host000000000002'), + renewal('user-b', 'host000000000003') + ]) + + expect(outcomes).toEqual(['renewed', 'renewed', 'renewed']) + expect(probe.statements).toHaveLength(1) + expect(probe.statements[0]!.sql).toBe(CONTROL_RENEWAL_BATCH_SQL) + expect(probe.statements[0]!.params[0]).toEqual(['user-a', 'user-a', 'user-b']) + expect(probe.statements[0]!.params[4]).toEqual([expiresAt, expiresAt, expiresAt]) + }) + + it('locks assignment rows in primary-key order and still answers in input order', async () => { + const probe = new RenewalStatementProbe((userId) => + userId === 'user-b' ? 'control_activity_moved' : 'renewed' + ) + const store = new RelayAssignmentStore(probe, () => now) + + const outcomes = await store.renewControlActivities([ + renewal('user-c', 'host000000000003'), + renewal('user-a', 'host000000000002'), + renewal('user-b', 'host000000000001'), + renewal('user-a', 'host000000000001') + ]) + + // (user_id, relay_host_id) is the primary key of relay_assignments, and the + // statement's ORDER BY repeats it: no batch can queue against another in a + // different sequence. + expect(probe.statements[0]!.params[0]).toEqual(['user-a', 'user-a', 'user-b', 'user-c']) + expect(probe.statements[0]!.params[1]).toEqual([ + 'host000000000001', + 'host000000000002', + 'host000000000001', + 'host000000000003' + ]) + expect(outcomes).toEqual([ + 'renewed', + 'renewed', + 'control_activity_moved', + 'renewed' + ]) + }) + + it('keeps a malformed request out of the statement and fails only that row', async () => { + const probe = new RenewalStatementProbe(() => 'renewed') + const store = new RelayAssignmentStore(probe, () => now) + + const outcomes = await store.renewControlActivities([ + renewal('user-a', 'host000000000001'), + renewal('user-a', 'host000000000002', now + ASSIGNMENT_LIMITS.activityLeaseMs * 10), + { ...renewal('user-a', 'host000000000003'), activityId: '' }, + renewal('user-a', 'host000000000004') + ]) + + expect(outcomes).toEqual([ + 'renewed', + 'invalid_activity_expiry', + 'invalid_activity_id', + 'renewed' + ]) + expect(probe.statements[0]!.params[1]).toEqual(['host000000000001', 'host000000000004']) + }) + + it('degrades to one statement per host when the batch statement fails', async () => { + const probe = new RenewalStatementProbe(() => 'renewed') + probe.failuresRemaining = 1 + const store = new RelayAssignmentStore(probe, () => now) + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined) + try { + const outcomes = await store.renewControlActivities([ + renewal('user-a', 'host000000000001'), + renewal('user-a', 'host000000000002') + ]) + + expect(outcomes).toEqual(['renewed', 'renewed']) + expect(probe.statements).toHaveLength(3) + expect(probe.statements[1]!.params[1]).toEqual(['host000000000001']) + expect(probe.statements[2]!.params[1]).toEqual(['host000000000002']) + expect(JSON.parse(String(warn.mock.calls[0]![0]))).toMatchObject({ + event: 'orca_relay_control_renewal_batch_failed', + rows: 2 + }) + } finally { + warn.mockRestore() + } + }) + + it('reports a host that fails its own fallback statement without touching the rest', async () => { + const probe = new RenewalStatementProbe(() => 'renewed') + probe.failuresRemaining = 2 + const store = new RelayAssignmentStore(probe, () => now) + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined) + try { + const outcomes = await store.renewControlActivities([ + renewal('user-a', 'host000000000001'), + renewal('user-a', 'host000000000002') + ]) + + expect(outcomes.filter((outcome) => outcome === 'renewed')).toHaveLength(1) + expect(outcomes.filter((outcome) => outcome === 'database_error')).toHaveLength(1) + } finally { + warn.mockRestore() + } + }) + + it('reports a contended assignment row apart from a missing one', async () => { + const probe = new RenewalStatementProbe((userId) => + userId === 'user-b' ? 'assignment_lock_unavailable' : 'renewed' + ) + const store = new RelayAssignmentStore(probe, () => now) + + const outcomes = await store.renewControlActivities([ + renewal('user-a', 'host000000000001'), + renewal('user-b', 'host000000000002') + ]) + + // Retryable: SKIP LOCKED passed over the row rather than queueing the whole + // flush behind whoever held it. + expect(outcomes).toEqual(['renewed', 'assignment_lock_unavailable']) + }) + + it('counts a lone renewal that threw before rethrowing it', async () => { + const probe = new RenewalStatementProbe(() => 'renewed') + probe.failuresRemaining = 1 + const recordControlRenewal = vi.fn() + const store = new RelayAssignmentStore(probe, () => now, { recordControlRenewal }) + + // A one-row flush keeps the pre-batch contract and rethrows, but the metric + // still owes an outcome for the attempt. + await expect( + store.renewControlActivities([renewal('user-a', 'host000000000001')]) + ).rejects.toThrow('statement timeout') + + expect(recordControlRenewal).toHaveBeenCalledTimes(1) + expect(recordControlRenewal.mock.calls[0]![1]).toBe('database_error') + }) + + it('counts a batch in which every row was rejected before the statement', async () => { + const probe = new RenewalStatementProbe(() => 'renewed') + const recordControlRenewal = vi.fn() + const store = new RelayAssignmentStore(probe, () => now, { recordControlRenewal }) + + const outcomes = await store.renewControlActivities([ + { ...renewal('user-a', 'host000000000001'), activityId: '' }, + renewal('user-a', 'host000000000002', now - 1) + ]) + + expect(outcomes).toEqual(['invalid_activity_id', 'invalid_activity_expiry']) + expect(probe.statements).toHaveLength(0) + expect(recordControlRenewal.mock.calls.map((call) => call[1])).toEqual([ + 'invalid_activity_id', + 'invalid_activity_expiry' + ]) + }) + + it('counts one renewal metric per row against the flush latency', async () => { + const probe = new RenewalStatementProbe((userId) => + userId === 'user-b' ? 'assignment_not_found' : 'renewed' + ) + const recordControlRenewal = vi.fn() + const store = new RelayAssignmentStore(probe, () => now, { recordControlRenewal }) + + await store.renewControlActivities([ + renewal('user-a', 'host000000000001'), + renewal('user-b', 'host000000000002') + ]) + + expect(recordControlRenewal).toHaveBeenCalledTimes(2) + expect(recordControlRenewal.mock.calls.map((call) => call[1])).toEqual([ + 'renewed', + 'assignment_not_found' + ]) + }) +}) + +describe('batched control renewals on SQLite', () => { + it('renews every host through the transactional path', async () => { + let clock = now + const database = await openInMemoryRelayDatabase() + try { + const store = new RelayAssignmentStore(database, () => clock) + await store.reconcileCells([ + { id: 'cell-a', url: 'https://relay-a.example.com', capacityRequests: 10 } + ]) + const hosts = ['host000000000001', 'host000000000002'] + const requests = [] + for (const relayHostId of hosts) { + const identity = { userId: 'user-a', relayHostId } + const assignment = await store.assign(identity) + await store.activateControl(identity, { + cellId: assignment.cellId, + assignmentEpoch: assignment.assignmentEpoch, + generation: 1 + }) + requests.push({ + identity, + activityId: `control:${assignment.cellId}:1`, + cellId: assignment.cellId, + expiresAt: clock + 105_000 + }) + } + // A host with no assignment at all must not cost the others their renewal. + requests.push({ + identity: { userId: 'user-a', relayHostId: 'host000000000009' }, + activityId: 'control:cell-a:1', + cellId: 'cell-a', + expiresAt: clock + 105_000 + }) + clock += 1_000 + + const outcomes = await store.renewControlActivities(requests) + + expect(outcomes).toEqual(['renewed', 'renewed', 'assignment_not_found']) + const leases = await database.query( + `SELECT relay_host_id, expires_at FROM relay_assignment_activity_leases + WHERE user_id = ? ORDER BY relay_host_id ASC`, + ['user-a'] + ) + expect(leases.map((lease) => Number(lease.expires_at))).toEqual([ + now + 105_000, + now + 105_000 + ]) + } finally { + await database.close() + } + }) +}) diff --git a/cloud/apps/relay/src/control-renewal-batch.test.ts b/cloud/apps/relay/src/control-renewal-batch.test.ts new file mode 100644 index 00000000000..10178b88476 --- /dev/null +++ b/cloud/apps/relay/src/control-renewal-batch.test.ts @@ -0,0 +1,231 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { + CONTROL_RENEWAL_BATCH_INTERVAL_MS, + CONTROL_RENEWAL_BATCH_MAX_ROWS, + ControlRenewalBatch, + type ControlRenewalFlush +} from './control-renewal-batch.js' +import type { + ControlRenewalOutcome, + ControlRenewalRequest +} from './control-renewal-statement.js' + +// Settles into the outcome the caller saw, attached at enqueue so a rejection is +// never momentarily unhandled. +function outcomeOf(renewal: Promise): Promise { + return renewal.then( + () => 'renewed', + (error: unknown) => String((error as { message?: unknown }).message) + ) +} + +function request( + host: string, + expiresAt = 1_000, + activityId = 'control:cell-a:1' +): ControlRenewalRequest { + return { + identity: { userId: 'user-a', relayHostId: host }, + activityId, + cellId: 'cell-a', + expiresAt + } +} + +describe('control renewal batch', () => { + beforeEach(() => vi.useFakeTimers()) + afterEach(() => { + vi.clearAllTimers() + vi.useRealTimers() + }) + + it('spends one call on every renewal that came due in the window', async () => { + const renew = vi.fn(async (rows: readonly ControlRenewalRequest[]) => + rows.map((): ControlRenewalOutcome => 'renewed') + ) + const batch = new ControlRenewalBatch(renew) + const settled = [ + batch.enqueue(request('host0000000000a1')), + batch.enqueue(request('host0000000000a2')), + batch.enqueue(request('host0000000000a3')) + ] + + expect(renew).not.toHaveBeenCalled() + await vi.advanceTimersByTimeAsync(CONTROL_RENEWAL_BATCH_INTERVAL_MS) + await expect(Promise.all(settled)).resolves.toEqual([undefined, undefined, undefined]) + expect(renew).toHaveBeenCalledOnce() + expect(renew.mock.calls[0]![0].map((row) => row.identity.relayHostId)).toEqual([ + 'host0000000000a1', + 'host0000000000a2', + 'host0000000000a3' + ]) + }) + + it('routes each outcome back to the caller that asked for it', async () => { + const outcomes: ControlRenewalOutcome[] = [ + 'renewed', + 'assignment_not_found', + 'control_activity_moved' + ] + const batch = new ControlRenewalBatch(async () => outcomes) + const first = outcomeOf(batch.enqueue(request('host0000000000b1'))) + const second = outcomeOf(batch.enqueue(request('host0000000000b2'))) + const third = outcomeOf(batch.enqueue(request('host0000000000b3'))) + + await vi.advanceTimersByTimeAsync(CONTROL_RENEWAL_BATCH_INTERVAL_MS) + + await expect(Promise.all([first, second, third])).resolves.toEqual([ + 'renewed', + 'assignment_not_found', + 'control_activity_moved' + ]) + }) + + it('flushes on reaching the row ceiling instead of waiting out the window', async () => { + const renew = vi.fn(async (rows: readonly ControlRenewalRequest[]) => + rows.map((): ControlRenewalOutcome => 'renewed') + ) + const batch = new ControlRenewalBatch(renew) + for (let row = 0; row < CONTROL_RENEWAL_BATCH_MAX_ROWS - 1; row++) { + void batch.enqueue(request(`host${String(row).padStart(12, '0')}`)) + } + expect(renew).not.toHaveBeenCalled() + + void batch.enqueue(request('host0000000000zz')) + await vi.advanceTimersByTimeAsync(0) + + expect(renew).toHaveBeenCalledOnce() + expect(renew.mock.calls[0]![0]).toHaveLength(CONTROL_RENEWAL_BATCH_MAX_ROWS) + // The window timer must not fire a second, empty statement. + await vi.advanceTimersByTimeAsync(CONTROL_RENEWAL_BATCH_INTERVAL_MS) + expect(renew).toHaveBeenCalledOnce() + }) + + it('does not hold a new window behind a statement still in PostgreSQL', async () => { + let release!: (outcomes: ControlRenewalOutcome[]) => void + const renew = vi + .fn<(rows: readonly ControlRenewalRequest[]) => Promise>() + .mockImplementationOnce( + async () => await new Promise((resolve) => (release = resolve)) + ) + .mockResolvedValue(['renewed']) + const batch = new ControlRenewalBatch(renew) + const stalled = batch.enqueue(request('host0000000000c1')) + await vi.advanceTimersByTimeAsync(CONTROL_RENEWAL_BATCH_INTERVAL_MS) + + const next = batch.enqueue(request('host0000000000c2')) + await vi.advanceTimersByTimeAsync(CONTROL_RENEWAL_BATCH_INTERVAL_MS) + + expect(renew).toHaveBeenCalledTimes(2) + await expect(next).resolves.toBeUndefined() + release(['renewed']) + await expect(stalled).resolves.toBeUndefined() + }) + + it('reports the driver failure to every caller in the flush', async () => { + const batch = new ControlRenewalBatch(async () => { + throw new Error('pool timeout') + }) + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined) + try { + const first = outcomeOf(batch.enqueue(request('host0000000000d1'))) + const second = outcomeOf(batch.enqueue(request('host0000000000d2'))) + await vi.advanceTimersByTimeAsync(CONTROL_RENEWAL_BATCH_INTERVAL_MS) + + await expect(Promise.all([first, second])).resolves.toEqual([ + 'pool timeout', + 'pool timeout' + ]) + } finally { + warn.mockRestore() + } + }) + + it('supersedes a second attempt for one lease and answers both callers', async () => { + const renew = vi.fn(async (rows: readonly ControlRenewalRequest[]) => + rows.map((): ControlRenewalOutcome => 'renewed') + ) + const batch = new ControlRenewalBatch(renew) + const earlier = batch.enqueue(request('host0000000000e1', 1_000)) + const later = batch.enqueue(request('host0000000000e1', 2_000)) + + await vi.advanceTimersByTimeAsync(CONTROL_RENEWAL_BATCH_INTERVAL_MS) + + expect(renew.mock.calls[0]![0]).toEqual([ + expect.objectContaining({ expiresAt: 2_000 }) + ]) + await expect(earlier).resolves.toBeUndefined() + await expect(later).resolves.toBeUndefined() + }) + + it('holds a second activity for one host back to the next flush', async () => { + const renew = vi.fn(async (rows: readonly ControlRenewalRequest[]) => + rows.map((): ControlRenewalOutcome => 'renewed') + ) + const batch = new ControlRenewalBatch(renew) + const first = batch.enqueue(request('host0000000000h1', 1_000, 'control:cell-a:1')) + const second = batch.enqueue(request('host0000000000h1', 1_000, 'control:cell-a:2')) + const other = batch.enqueue(request('host0000000000h2')) + + await vi.advanceTimersByTimeAsync(CONTROL_RENEWAL_BATCH_INTERVAL_MS) + + // One statement updates a host's assignment row once, so the host appears in + // one flush only; the newer generation leads the next one. + expect(renew.mock.calls[0]![0].map((row) => row.activityId)).toEqual([ + 'control:cell-a:1', + 'control:cell-a:1' + ]) + await expect(Promise.all([first, other])).resolves.toEqual([undefined, undefined]) + + await vi.advanceTimersByTimeAsync(CONTROL_RENEWAL_BATCH_INTERVAL_MS) + + expect(renew).toHaveBeenCalledTimes(2) + expect(renew.mock.calls[1]![0].map((row) => row.activityId)).toEqual(['control:cell-a:2']) + await expect(second).resolves.toBeUndefined() + }) + + it('stays quiet for a fast flush that renewed everything', async () => { + const flushes: ControlRenewalFlush[] = [] + const batch = new ControlRenewalBatch( + async () => ['renewed'], + () => ({ cellId: 'cell-a' }), + (flush) => flushes.push(flush) + ) + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined) + try { + void batch.enqueue(request('host0000000000f1')) + await vi.advanceTimersByTimeAsync(CONTROL_RENEWAL_BATCH_INTERVAL_MS) + + expect(warn).not.toHaveBeenCalled() + expect(flushes).toEqual([ + { rows: 1, durationMs: expect.any(Number), outcomes: { renewed: 1 } } + ]) + } finally { + warn.mockRestore() + } + }) + + it('logs one line with the outcome counts when a flush did not renew everything', async () => { + const batch = new ControlRenewalBatch( + async () => ['renewed', 'control_activity_not_found'], + () => ({ cellId: 'cell-a' }) + ) + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined) + try { + void outcomeOf(batch.enqueue(request('host0000000000g1'))) + const missing = outcomeOf(batch.enqueue(request('host0000000000g2'))) + await vi.advanceTimersByTimeAsync(CONTROL_RENEWAL_BATCH_INTERVAL_MS) + await expect(missing).resolves.toBe('control_activity_not_found') + + expect(warn).toHaveBeenCalledOnce() + expect(JSON.parse(String(warn.mock.calls[0]![0]))).toMatchObject({ + event: 'orca_relay_control_renewal_flush', + cellId: 'cell-a', + rows: 2, + outcomes: { renewed: 1, control_activity_not_found: 1 } + }) + } finally { + warn.mockRestore() + } + }) +}) diff --git a/cloud/apps/relay/src/control-renewal-batch.ts b/cloud/apps/relay/src/control-renewal-batch.ts new file mode 100644 index 00000000000..984333b2ca6 --- /dev/null +++ b/cloud/apps/relay/src/control-renewal-batch.ts @@ -0,0 +1,148 @@ +import { performance } from 'node:perf_hooks' +import type { + ControlRenewalOutcome, + ControlRenewalRequest +} from './control-renewal-statement.js' + +// One flush per second turns the fleet's control-lease write rate into a +// function of the cell count rather than the host count: a cell's ~10 due +// renewals per second become one write transaction instead of ten. Well inside +// the 105s lease runway, so a host that misses a window is never at risk. +export const CONTROL_RENEWAL_BATCH_INTERVAL_MS = 1_000 +// Ceiling on the parameter arrays. Row locks live until the statement commits, +// so this is what bounds how long one flush holds them: measured at 11.5ms for +// 200 rows against a 20,000-row table, and 9.4ms with a host wedged in a +// per-host transaction. +export const CONTROL_RENEWAL_BATCH_MAX_ROWS = 200 +// A flush slower than this is the only latency worth a line; the metrics event +// carries the distribution. +const CONTROL_RENEWAL_SLOW_FLUSH_MS = 250 + +export type ControlRenewalFlush = { + rows: number + durationMs: number + outcomes: Record +} + +type PendingWaiter = { resolve: () => void; reject: (error: unknown) => void } + +type PendingRenewal = { request: ControlRenewalRequest; waiters: PendingWaiter[] } + +type QueuedRenewal = { request: ControlRenewalRequest; waiter: PendingWaiter } + +// Per host, not per activity: one statement updates a host's assignment row +// once, so two activities for the same host must not share a flush. +function pendingKey(request: ControlRenewalRequest): string { + return [request.identity.userId, request.identity.relayHostId].join('\u0000') +} + +// Collects the control-lease renewals a cell owes and spends one statement on +// them. Each caller still gets the single-renewal contract: the promise resolves +// on `renewed` and rejects with the outcome as its message otherwise, so callers +// keep their per-session error routing unchanged. +export class ControlRenewalBatch { + private pending = new Map() + // Renewals a host cannot contribute to the flush being built; they open the + // next one. + private deferred: QueuedRenewal[] = [] + private timer: ReturnType | null = null + + constructor( + private readonly renew: ( + rows: readonly ControlRenewalRequest[] + ) => Promise, + private readonly logFields: () => Record = () => ({}), + private readonly observe?: (flush: ControlRenewalFlush) => void + ) {} + + enqueue(request: ControlRenewalRequest): Promise { + return new Promise((resolve, reject) => { + this.admit({ request, waiter: { resolve, reject } }) + }) + } + + private admit(queued: QueuedRenewal): void { + const key = pendingKey(queued.request) + const existing = this.pending.get(key) + if (existing && existing.request.activityId !== queued.request.activityId) { + this.deferred.push(queued) + this.scheduleFlush() + return + } + if (existing) { + // A second attempt at the same lease inside one window supersedes the + // first expiry; both callers still hear the outcome they waited for. + existing.request = { + ...queued.request, + expiresAt: Math.max(existing.request.expiresAt, queued.request.expiresAt) + } + existing.waiters.push(queued.waiter) + return + } + this.pending.set(key, { request: queued.request, waiters: [queued.waiter] }) + if (this.pending.size >= CONTROL_RENEWAL_BATCH_MAX_ROWS) { + void this.flush() + return + } + this.scheduleFlush() + } + + private scheduleFlush(): void { + this.timer ??= setTimeout(() => { + this.timer = null + void this.flush() + }, CONTROL_RENEWAL_BATCH_INTERVAL_MS) + this.timer.unref?.() + } + + // Flushes run concurrently on purpose: a statement stalled in PostgreSQL must + // not hold back the renewals that came due while it was waiting. + async flush(): Promise { + if (this.timer) { + clearTimeout(this.timer) + this.timer = null + } + const batch = [...this.pending.values()] + this.pending = new Map() + // Re-admitted against the empty map, so a host deferred out of this flush + // leads the next one. + const deferred = this.deferred + this.deferred = [] + for (const queued of deferred) this.admit(queued) + if (batch.length === 0) return + const startedAt = performance.now() + let outcomes: ControlRenewalOutcome[] + try { + outcomes = await this.renew(batch.map((entry) => entry.request)) + } catch (error) { + for (const entry of batch) for (const waiter of entry.waiters) waiter.reject(error) + this.report(batch.length, performance.now() - startedAt, { flush_failed: batch.length }) + return + } + const counts: Record = {} + for (const [index, entry] of batch.entries()) { + const outcome = outcomes[index] ?? 'database_error' + counts[outcome] = (counts[outcome] ?? 0) + 1 + for (const waiter of entry.waiters) { + if (outcome === 'renewed') waiter.resolve() + else waiter.reject(new Error(outcome)) + } + } + this.report(batch.length, performance.now() - startedAt, counts) + } + + private report(rows: number, durationMs: number, outcomes: Record): void { + this.observe?.({ rows, durationMs, outcomes }) + const renewed = outcomes.renewed ?? 0 + if (durationMs <= CONTROL_RENEWAL_SLOW_FLUSH_MS && renewed === rows) return + console.warn( + JSON.stringify({ + event: 'orca_relay_control_renewal_flush', + ...this.logFields(), + rows, + durationMs: Math.round(durationMs), + outcomes + }) + ) + } +} diff --git a/cloud/apps/relay/src/control-renewal-postgres.test.ts b/cloud/apps/relay/src/control-renewal-postgres.test.ts index fb49e3af3a2..354c9edf44a 100644 --- a/cloud/apps/relay/src/control-renewal-postgres.test.ts +++ b/cloud/apps/relay/src/control-renewal-postgres.test.ts @@ -1,8 +1,11 @@ +import { performance } from 'node:perf_hooks' import { ASSIGNMENT_LIMITS, RELAY_PROTOCOL_LIMITS } from '@orca-cloud/relay-contract' import { afterAll, beforeAll, describe, expect, it } from 'vitest' import { RelayAssignmentStore } from './assignment-store.js' +import { CONTROL_RENEWAL_BATCH_SQL } from './control-renewal-statement.js' import { openRelayDatabase, + POSTGRES_LOCK_TIMEOUT_MS, type RelayDatabase, type RelayLockOptions, type SqlRow @@ -22,7 +25,9 @@ const targetCell = { capacityRequests: 100 } const userId = 'control-renewal-postgres-user' -const identities = Array.from({ length: 6 }, (_, index) => ({ +// Indexes 0-5 belong to the single-renewal cases below, which mutate their +// host's migration and lease state; the batch cases own 6-13. +const identities = Array.from({ length: 14 }, (_, index) => ({ userId, relayHostId: `controlrenewal${index + 1}` })) @@ -72,7 +77,7 @@ class StallFirstRenewalQueryDatabase implements RelayDatabase { constructor(private readonly database: RelayDatabase) {} async query(sql: string, params?: unknown[]): Promise { - if (this.stallNext && sql.includes('WITH assignment_state AS MATERIALIZED')) { + if (this.stallNext && sql === CONTROL_RENEWAL_BATCH_SQL) { this.stallNext = false this.stalled.resolve() await this.continue.promise @@ -103,7 +108,7 @@ class RenewalQueryProbeDatabase implements RelayDatabase { constructor(private readonly database: RelayDatabase) {} async query(sql: string, params?: unknown[]): Promise { - if (sql.includes('WITH assignment_state AS MATERIALIZED')) this.renewalQueries++ + if (sql === CONTROL_RENEWAL_BATCH_SQL) this.renewalQueries++ return await this.database.query(sql, params) } @@ -332,6 +337,164 @@ describePostgres('PostgreSQL control renewal', () => { ).rejects.toThrow('invalid_activity_expiry') }) + it('renews every due host in one autocommitted statement', async () => { + const probe = new RenewalQueryProbeDatabase(database) + const store = new RelayAssignmentStore(probe, () => now) + const batch = identities.slice(6, 10) + now += 30_000 + const expiresAt = now + 105_000 + + const outcomes = await store.renewControlActivities( + batch.map((identity) => ({ + identity, + activityId: controlId(sourceCell.id), + cellId: sourceCell.id, + expiresAt + })) + ) + + expect(outcomes).toEqual(['renewed', 'renewed', 'renewed', 'renewed']) + expect(probe.renewalQueries).toBe(1) + expect(probe.transactions).toBe(0) + const leases = await database.query( + `SELECT relay_host_id, expires_at FROM relay_assignment_activity_leases + WHERE user_id = ? AND activity_id = ? ORDER BY relay_host_id ASC`, + [userId, controlId(sourceCell.id)] + ) + expect( + leases + .filter((lease) => + batch.some((identity) => identity.relayHostId === lease.relay_host_id) + ) + .map((lease) => Number(lease.expires_at)) + ).toEqual([expiresAt, expiresAt, expiresAt, expiresAt]) + }) + + it('reports each host its own verdict inside one batch', async () => { + const store = new RelayAssignmentStore(database, () => now) + now += 30_000 + const expiresAt = now + 105_000 + const live = identities[10]! + const absent = { userId, relayHostId: 'controlrenewalgone' } + + const outcomes = await store.renewControlActivities([ + { identity: absent, activityId: controlId(sourceCell.id), cellId: sourceCell.id, expiresAt }, + { identity: live, activityId: controlId(sourceCell.id), cellId: sourceCell.id, expiresAt }, + { + identity: live, + activityId: controlId(targetCell.id), + cellId: targetCell.id, + expiresAt + } + ]) + + expect(outcomes).toEqual([ + 'assignment_not_found', + 'renewed', + 'activity_cell_not_authoritative' + ]) + }) + + it('passes over a host whose assignment row is held and renews the rest', async () => { + const store = new RelayAssignmentStore(database, () => now) + now += 30_000 + const expiresAt = now + 105_000 + const held = identities[11]! + const free = identities[12]! + const locked = signal() + const release = signal() + // Holds the row the way every per-host transactional path does. + const holder = database.transaction(async (transaction) => { + await transaction.queryLocked( + `SELECT * FROM relay_assignments WHERE user_id = ? AND relay_host_id = ?`, + [held.userId, held.relayHostId] + ) + locked.resolve() + await release.promise + }) + await locked.promise + + const startedAt = performance.now() + const outcomes = await store.renewControlActivities( + [held, free].map((identity) => ({ + identity, + activityId: controlId(sourceCell.id), + cellId: sourceCell.id, + expiresAt + })) + ) + const elapsedMs = performance.now() - startedAt + release.resolve() + await holder + + expect(outcomes).toEqual(['assignment_lock_unavailable', 'renewed']) + // It skipped rather than queued: a blocking FOR UPDATE would have spent the + // pool's whole lock_timeout here and failed the free host too. + expect(elapsedMs).toBeLessThan(POSTGRES_LOCK_TIMEOUT_MS) + const lease = ( + await database.query( + `SELECT expires_at FROM relay_assignment_activity_leases + WHERE user_id = ? AND relay_host_id = ? AND activity_id = ?`, + [free.userId, free.relayHostId, controlId(sourceCell.id)] + ) + )[0] + expect(Number(lease!.expires_at)).toBe(expiresAt) + }) + + it('renews both of one host\u2019s control leases in a single batch', async () => { + const store = new RelayAssignmentStore(database, () => now) + const identity = identities[13]! + // Two live control leases on one host. Written directly because + // activateControl retires the prior generation, and what is under test is the + // statement's row-wise behaviour, not how the second lease came to exist. + await database.query( + `INSERT INTO relay_assignment_activity_leases + (user_id, relay_host_id, activity_id, activity_kind, cell_id, + request_units, expires_at, updated_at) + VALUES (?, ?, ?, 'control', ?, 1, ?, ?)`, + [ + identity.userId, + identity.relayHostId, + `control:${sourceCell.id}:2`, + sourceCell.id, + now, + now + ] + ) + now += 30_000 + const expiresAt = now + 105_000 + + const outcomes = await store.renewControlActivities( + [1, 2].map((generation) => ({ + identity, + activityId: `control:${sourceCell.id}:${generation}`, + cellId: sourceCell.id, + expiresAt: expiresAt - generation + })) + ) + + expect(outcomes).toEqual(['renewed', 'renewed']) + const leases = await database.query( + `SELECT activity_id, expires_at FROM relay_assignment_activity_leases + WHERE user_id = ? AND relay_host_id = ? ORDER BY activity_id ASC`, + [identity.userId, identity.relayHostId] + ) + expect(leases.map((lease) => Number(lease.expires_at))).toEqual([ + expiresAt - 1, + expiresAt - 2 + ]) + // The assignment row is written once, carrying the later of the two. + const row = ( + await database.query( + `SELECT lease_expires_at, last_activity_at FROM relay_assignments + WHERE user_id = ? AND relay_host_id = ?`, + [identity.userId, identity.relayHostId] + ) + )[0] + expect(Number(row!.lease_expires_at)).toBe(expiresAt - 1) + expect(Number(row!.last_activity_at)).toBe(now) + }) + it('uses one autocommitted PostgreSQL statement for a steady renewal', async () => { const probe = new RenewalQueryProbeDatabase(database) const store = new RelayAssignmentStore(probe, () => now) diff --git a/cloud/apps/relay/src/control-renewal-statement.ts b/cloud/apps/relay/src/control-renewal-statement.ts new file mode 100644 index 00000000000..486f825c10d --- /dev/null +++ b/cloud/apps/relay/src/control-renewal-statement.ts @@ -0,0 +1,230 @@ +import type { AssignmentIdentity } from './assignment-identity-queue.js' +import type { SqlRow } from './database.js' + +export type ControlRenewalOutcome = + | 'renewed' + | 'assignment_not_found' + | 'activity_cell_not_authoritative' + | 'control_activity_not_found' + | 'control_activity_moved' + // The host's assignment row was already locked by one of the per-host + // transactional paths. Retryable, and never a reason to close a control: the + // next tick is 15s away and the lease has 105s on it. + | 'assignment_lock_unavailable' + // Decided per row before the statement runs, so one malformed request cannot + // cost the rest of the batch its renewal. + | 'invalid_activity_id' + | 'invalid_activity_expiry' + | 'database_error' + +// Outcomes the statement itself can report. `database_error` is raised by the +// driver, and `invalid_activity_expiry` is decided per row before the statement +// is built, so neither can come back as a row. +export const CONTROL_RENEWAL_STATEMENT_OUTCOMES = new Set([ + 'renewed', + 'assignment_not_found', + 'activity_cell_not_authoritative', + 'control_activity_not_found', + 'control_activity_moved', + 'assignment_lock_unavailable' +]) + +export type ControlRenewalRequest = { + identity: AssignmentIdentity + activityId: string + cellId: string + expiresAt: number +} + +// LOCK ORDER - (user_id, relay_host_id), the primary key of relay_assignments, +// applied here and repeated as the statement's ORDER BY so it holds whether the +// planner walks the primary-key index or sorts under the LockRows node. +// +// The batch never waits for an assignment row: SKIP LOCKED reports a contended +// host separately instead. That is what bounds how long a flush holds its locks +// to its own execution time, because row locks live until the statement commits, +// and it is why one host wedged in a per-host transaction cannot stall the +// renewals of every other host sharing the flush. +// +// With no wait on the assignment pass, the deadlock question reduces to the two +// later passes. Every writer in this store locks a host's assignment row before +// that host's lease rows (`assignmentRow` then `lockAssignmentActivities`), and +// a host whose assignment row is held was skipped, so the batch never reaches +// that host's lease: the lease pass cannot wait either. +// `markMigrationTargetRegistered` is the one writer that locks a migration row +// without the assignment row first. It takes no further locks, so it can delay a +// mid-migration row by up to the pool's lock_timeout but cannot close a cycle. +export function orderedControlRenewalRows( + rows: readonly Row[] +): Row[] { + return [...rows].sort( + (left, right) => + left.identity.userId.localeCompare(right.identity.userId) || + left.identity.relayHostId.localeCompare(right.identity.relayHostId) + ) +} + +// One statement renewing every due control lease on this cell, row-wise over the +// unnested parameter arrays. Logic per row is what the single-row predecessor +// did: lock the assignment, admit the caller's cell either as the current cell or +// as the source of an active forward migration, lock that host's control lease, +// push both expiries forward, and report one outcome. The one addition is +// `present_assignment`, an unlocked probe that separates a host with no +// assignment row at all from one whose row SKIP LOCKED passed over - the first +// closes the control, the second retries. +export const CONTROL_RENEWAL_BATCH_SQL = `WITH renewal_input AS MATERIALIZED ( + SELECT + renewal.ordinality AS row_index, + renewal.user_id, + renewal.relay_host_id, + renewal.activity_id, + renewal.cell_id, + renewal.expires_at + FROM unnest(?::text[], ?::text[], ?::text[], ?::text[], ?::bigint[]) + WITH ORDINALITY AS renewal( + user_id, relay_host_id, activity_id, cell_id, expires_at, ordinality + ) + ), present_assignment AS MATERIALIZED ( + SELECT input.row_index + FROM renewal_input input + JOIN relay_assignments assignment + ON assignment.user_id = input.user_id + AND assignment.relay_host_id = input.relay_host_id + ), assignment_state AS MATERIALIZED ( + SELECT input.row_index, assignment.cell_id, assignment.assignment_epoch + FROM renewal_input input + JOIN relay_assignments assignment + ON assignment.user_id = input.user_id + AND assignment.relay_host_id = input.relay_host_id + ORDER BY assignment.user_id, assignment.relay_host_id + FOR UPDATE OF assignment SKIP LOCKED + ), migration_state AS MATERIALIZED ( + SELECT locked.row_index + FROM assignment_state locked + JOIN renewal_input input ON input.row_index = locked.row_index + JOIN relay_assignment_migrations migration + ON migration.user_id = input.user_id + AND migration.relay_host_id = input.relay_host_id + AND migration.source_cell_id = input.cell_id + AND migration.target_cell_id = locked.cell_id + AND migration.assignment_epoch = locked.assignment_epoch + AND migration.completed_at IS NULL AND migration.aborted_at IS NULL + ORDER BY migration.user_id, migration.relay_host_id + FOR UPDATE OF migration + ), authorization_state AS MATERIALIZED ( + SELECT locked.row_index + FROM assignment_state locked + JOIN renewal_input input ON input.row_index = locked.row_index + WHERE locked.cell_id = input.cell_id + OR EXISTS ( + SELECT 1 FROM migration_state moving + WHERE moving.row_index = locked.row_index + ) + ), lease_state AS MATERIALIZED ( + SELECT authorized.row_index, lease.activity_kind, lease.cell_id + FROM authorization_state authorized + JOIN renewal_input input ON input.row_index = authorized.row_index + JOIN relay_assignment_activity_leases lease + ON lease.user_id = input.user_id + AND lease.relay_host_id = input.relay_host_id + AND lease.activity_id = input.activity_id + ORDER BY lease.user_id, lease.relay_host_id, lease.activity_id + FOR UPDATE OF lease + ), renewed_lease AS ( + UPDATE relay_assignment_activity_leases lease + SET expires_at = GREATEST(lease.expires_at, input.expires_at), + updated_at = GREATEST(lease.updated_at, ?) + FROM lease_state state + JOIN renewal_input input ON input.row_index = state.row_index + WHERE lease.user_id = input.user_id + AND lease.relay_host_id = input.relay_host_id + AND lease.activity_id = input.activity_id + AND state.activity_kind = 'control' AND state.cell_id = input.cell_id + RETURNING state.row_index + ), renewed_assignment AS ( + -- Grouped per host: an UPDATE whose FROM offers a target row more than + -- once applies one source row and returns one, so two leases on one + -- host would leave the assignment carrying the wrong expiry. The + -- aggregate hands it exactly one row, carrying the later expiry. + UPDATE relay_assignments assignment + SET lease_expires_at = GREATEST(assignment.lease_expires_at, renewed.expires_at), + last_activity_at = GREATEST(assignment.last_activity_at, ?) + FROM ( + SELECT input.user_id, input.relay_host_id, MAX(input.expires_at) AS expires_at + FROM renewed_lease renewed + JOIN renewal_input input ON input.row_index = renewed.row_index + GROUP BY input.user_id, input.relay_host_id + ) renewed + WHERE assignment.user_id = renewed.user_id + AND assignment.relay_host_id = renewed.relay_host_id + RETURNING renewed.user_id + ) + SELECT input.row_index, CASE + WHEN NOT EXISTS ( + SELECT 1 FROM present_assignment present + WHERE present.row_index = input.row_index + ) THEN 'assignment_not_found' + WHEN NOT EXISTS ( + SELECT 1 FROM assignment_state locked WHERE locked.row_index = input.row_index + ) THEN 'assignment_lock_unavailable' + WHEN NOT EXISTS ( + SELECT 1 FROM authorization_state authorized + WHERE authorized.row_index = input.row_index + ) THEN 'activity_cell_not_authoritative' + WHEN NOT EXISTS ( + SELECT 1 FROM lease_state state WHERE state.row_index = input.row_index + ) THEN 'control_activity_not_found' + WHEN EXISTS ( + SELECT 1 FROM lease_state state + WHERE state.row_index = input.row_index + AND (state.activity_kind <> 'control' OR state.cell_id <> input.cell_id) + ) THEN 'control_activity_moved' + -- Read from renewed_lease, which has one row per input row. The + -- assignment update collapses to one row per host, so it cannot answer + -- for a host that brought two leases to the same batch. + WHEN EXISTS ( + SELECT 1 FROM renewed_lease renewed + WHERE renewed.row_index = input.row_index + ) THEN 'renewed' + ELSE 'control_activity_not_found' + END AS outcome + FROM renewal_input input + ORDER BY input.row_index` + +export function controlRenewalBatchParams( + rows: readonly ControlRenewalRequest[], + now: number +): unknown[] { + return [ + rows.map((row) => row.identity.userId), + rows.map((row) => row.identity.relayHostId), + rows.map((row) => row.activityId), + rows.map((row) => row.cellId), + rows.map((row) => row.expiresAt), + now, + now + ] +} + +// Rows come back ordered by row_index, which is the 1-based position in the +// statement's parameter arrays. +export function readControlRenewalOutcomes( + rows: SqlRow[], + expected: number +): ControlRenewalOutcome[] { + if (rows.length !== expected) throw new Error('missing_control_renewal_outcome') + return rows.map((row, position) => { + if (Number(row.row_index) !== position + 1) { + throw new Error('misordered_control_renewal_outcome') + } + const outcome = row.outcome + if ( + typeof outcome !== 'string' || + !CONTROL_RENEWAL_STATEMENT_OUTCOMES.has(outcome as ControlRenewalOutcome) + ) { + throw new Error('invalid_control_renewal_outcome') + } + // SAFETY: the membership check above is what narrows this string. + return outcome as ControlRenewalOutcome + }) +} diff --git a/cloud/apps/relay/src/host-session-registry.test.ts b/cloud/apps/relay/src/host-session-registry.test.ts index 7fcff6a79f4..69697cce60e 100644 --- a/cloud/apps/relay/src/host-session-registry.test.ts +++ b/cloud/apps/relay/src/host-session-registry.test.ts @@ -12,6 +12,12 @@ import type WebSocket from 'ws' import type { RelayAssignmentStore } from './assignment-store.js' import type { RelayConfig } from './config.js' import type { RelayCredentialStore } from './credential-store.js' +import { CONTROL_RENEWAL_BATCH_INTERVAL_MS } from './control-renewal-batch.js' +import { + CONTROL_RENEWAL_STATEMENT_OUTCOMES, + type ControlRenewalOutcome, + type ControlRenewalRequest +} from './control-renewal-statement.js' import { HostSessionRegistry, type HostSession } from './host-session-registry.js' import { relayHostLogDigest } from './relay-host-log-digest.js' import type { RelayRuntimeObserver } from './relay-observability.js' @@ -23,6 +29,12 @@ import { import type { RelayTokenClaims } from './relay-token-verifier.js' import { ProcessQueuedByteBudget } from './splice-forwarder.js' +// A due renewal leaves the heartbeat as a batch enqueue, so the store only sees +// the tick once the batch window closes. +async function closeRenewalWindow(): Promise { + await vi.advanceTimersByTimeAsync(CONTROL_RENEWAL_BATCH_INTERVAL_MS) +} + class FakeSocket extends EventEmitter { readonly OPEN = 1 readonly CLOSING = 2 @@ -109,6 +121,7 @@ function createRegistry( activate: ActivateSession acquireActivity: ReturnType renewControlActivity: ReturnType + renewControlActivities: ReturnType releaseActivity: ReturnType observer: { recordAuth: ReturnType @@ -119,12 +132,38 @@ function createRegistry( const acquireActivity = vi.fn().mockResolvedValue(undefined) const renewControlActivity = vi.fn().mockResolvedValue(undefined) const releaseActivity = vi.fn().mockResolvedValue(true) + // Mirrors the store's own batch semantics over the single-renewal mock: a known + // outcome becomes that row's verdict, and any other failure reaches the caller + // as the driver's error. Keeps every per-call expectation below aimed at the + // renewal a session actually asked for. + const renewControlActivities = vi.fn( + async (rows: readonly ControlRenewalRequest[]): Promise => + await Promise.all( + rows.map(async (row): Promise => { + try { + await renewControlActivity(row.identity, { + activityId: row.activityId, + cellId: row.cellId, + expiresAt: row.expiresAt + }) + return 'renewed' + } catch (error) { + const message = String((error as { message?: unknown }).message) + if (!CONTROL_RENEWAL_STATEMENT_OUTCOMES.has(message as ControlRenewalOutcome)) { + throw error + } + return message as ControlRenewalOutcome + } + }) + ) + ) const assignments = { activateControl, markMigrationTargetRegistered: vi.fn().mockResolvedValue(undefined), resolve: vi.fn().mockResolvedValue({ cellId: config.cellId }), acquireActivity, renewControlActivity, + renewControlActivities, releaseActivity } as unknown as RelayAssignmentStore const observer = { @@ -176,6 +215,7 @@ function createRegistry( activate, acquireActivity, renewControlActivity, + renewControlActivities, releaseActivity, observer } @@ -689,7 +729,8 @@ describe('host session cleanup races', () => { expect(original).not.toBeNull() await activate(new FakeSocket() as unknown as WebSocket, identity, original, 2, false, 1) - vi.advanceTimersByTime(15_000) + await vi.advanceTimersByTimeAsync(15_000) + await closeRenewalWindow() expect(renewControlActivity).toHaveBeenCalledOnce() expect(renewControlActivity).toHaveBeenCalledWith( @@ -715,6 +756,7 @@ describe('host session cleanup races', () => { }) ) await vi.advanceTimersByTimeAsync(15_000) + await closeRenewalWindow() const replacement = new FakeSocket() await h.activate(replacement as unknown as WebSocket, identity, session, 1, true, 1) reject(new Error('activity_cell_not_authoritative')) @@ -737,6 +779,7 @@ describe('host session cleanup races', () => { }) ) await vi.advanceTimersByTimeAsync(15_000) + await closeRenewalWindow() h.registry.drainHost({ attemptId: 'attempt', userId: identity.sub, @@ -762,6 +805,7 @@ describe('host session cleanup races', () => { for (let interval = 0; interval < 4; interval++) { await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs) socket.emit('message', Buffer.from(JSON.stringify({ type: 'pong' })), false) + await closeRenewalWindow() } const pings = socket.send.mock.calls.filter((call) => String(call[0]).includes('"ping"')) @@ -791,8 +835,10 @@ describe('host session cleanup races', () => { try { await activate(socket as unknown as WebSocket, identity, null, 1, false, 1) await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs) + await closeRenewalWindow() expect(renewControlActivity).toHaveBeenCalledOnce() await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs) + await closeRenewalWindow() expect(renewControlActivity).toHaveBeenCalledTimes(2) } finally { warn.mockRestore() @@ -812,6 +858,7 @@ describe('host session cleanup races', () => { await activate(socket as unknown as WebSocket, identity, null, 1, false, 1) await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs * 2) + await closeRenewalWindow() expect(renewControlActivity).toHaveBeenCalledTimes(2) stalled.resolve(undefined) @@ -831,13 +878,16 @@ describe('host session cleanup races', () => { await activate(socket as unknown as WebSocket, identity, null, 1, false, 1) await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs * 2) + await closeRenewalWindow() expect(renewControlActivity).toHaveBeenCalledTimes(2) stalled.resolve(undefined) await vi.advanceTimersByTimeAsync(0) await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs) + await closeRenewalWindow() expect(renewControlActivity).toHaveBeenCalledTimes(2) await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs) + await closeRenewalWindow() expect(renewControlActivity).toHaveBeenCalledTimes(3) registry.drain(0) @@ -855,6 +905,7 @@ describe('host session cleanup races', () => { await activate(socket as unknown as WebSocket, identity, null, 1, false, 1) await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs) + await closeRenewalWindow() expect(acquireActivity).toHaveBeenCalledWith( { userId: identity.sub, relayHostId: identity.relayHostId }, @@ -880,6 +931,7 @@ describe('host session cleanup races', () => { await activate(socket as unknown as WebSocket, identity, null, 1, false, 1) await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs) + await closeRenewalWindow() expect(acquireActivity).not.toHaveBeenCalled() expect(socket.close).toHaveBeenCalledWith(RELAY_CLOSE_CODE.DRAINING, 'control activity moved') @@ -899,6 +951,7 @@ describe('host session cleanup races', () => { await activate(socket as unknown as WebSocket, identity, null, 1, false, 1) await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs) + await closeRenewalWindow() expect(socket.close).toHaveBeenCalledWith( RELAY_CLOSE_CODE.DRAINING, @@ -918,6 +971,7 @@ describe('host session cleanup races', () => { await activate(socket as unknown as WebSocket, identity, null, 1, false, 1) await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs) + await closeRenewalWindow() expect(socket.close).toHaveBeenCalledWith( RELAY_CLOSE_CODE.DRAINING, @@ -939,6 +993,7 @@ describe('host session cleanup races', () => { for (let interval = 0; interval < 3; interval++) { await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs) socket.emit('message', Buffer.from(JSON.stringify({ type: 'pong' })), false) + await closeRenewalWindow() } expect(renewControlActivity).toHaveBeenCalledTimes(2) @@ -966,6 +1021,7 @@ describe('control renewal cadence across a rebind', () => { const beat = async (target: FakeSocket): Promise => { await vi.advanceTimersByTimeAsync(ping) target.emit('message', Buffer.from(JSON.stringify({ type: 'pong' })), false) + await closeRenewalWindow() } // Age the session so its attempt counter is well above zero. @@ -993,6 +1049,57 @@ describe('control renewal cadence across a rebind', () => { }) }) +describe('control renewals shared by one batch', () => { + beforeEach(() => vi.useFakeTimers()) + afterEach(() => { + vi.clearAllTimers() + vi.useRealTimers() + }) + + it('renews two due hosts in one call and leaves a stale one alone', async () => { + const activateControl = vi + .fn() + .mockResolvedValueOnce('control:production-gce-c3:1') + .mockResolvedValueOnce('control:production-gce-c3:1') + const { registry, activate, renewControlActivities } = createRegistry(activateControl) + const other = { ...identity, sub: 'user-2', relayHostId: 'ponmlkjihgfedcba' } + const staleSocket = new FakeSocket() + const liveSocket = new FakeSocket() + await activate(staleSocket as unknown as WebSocket, identity, null, 1, false, 1) + await activate(liveSocket as unknown as WebSocket, other, null, 1, false, 1) + const stale = registry.get({ userId: identity.sub, relayHostId: identity.relayHostId })! + const live = registry.get({ userId: other.sub, relayHostId: other.relayHostId })! + + // Both come due inside the same window, and one socket goes away while the + // statement is still in PostgreSQL. + let release!: () => void + renewControlActivities.mockImplementationOnce( + async (rows: readonly ControlRenewalRequest[]) => { + staleSocket.close() + await new Promise((resolve) => (release = resolve)) + return rows.map((): ControlRenewalOutcome => 'renewed') + } + ) + await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs) + const staleDueAt = stale.activityRenewalDueAt + await closeRenewalWindow() + release() + await vi.advanceTimersByTimeAsync(0) + + expect(renewControlActivities).toHaveBeenCalledOnce() + expect( + renewControlActivities.mock.calls[0]![0].map( + (row: ControlRenewalRequest) => row.identity.relayHostId + ) + ).toEqual([identity.relayHostId, other.relayHostId]) + expect(live.activityRenewalCompletedAttempt).toBe(1) + expect(stale.activityRenewalCompletedAttempt).toBe(0) + expect(stale.activityRenewalDueAt).toBe(staleDueAt) + registry.drain(0) + vi.advanceTimersByTime(0) + }) +}) + describe('control lease recovery after the session is gone', () => { beforeEach(() => vi.useFakeTimers()) afterEach(() => { @@ -1019,6 +1126,7 @@ describe('control lease recovery after the session is gone', () => { new Promise((_resolve, reject) => (failRenewal = reject)) ) await vi.advanceTimersByTimeAsync(RELAY_PROTOCOL_LIMITS.controlPingIntervalMs) + await closeRenewalWindow() expect(renewControlActivity).toHaveBeenCalledOnce() const newer = new FakeSocket() diff --git a/cloud/apps/relay/src/host-session-registry.ts b/cloud/apps/relay/src/host-session-registry.ts index 35e3935fb7d..480b1b6b914 100644 --- a/cloud/apps/relay/src/host-session-registry.ts +++ b/cloud/apps/relay/src/host-session-registry.ts @@ -26,6 +26,7 @@ import type WebSocket from 'ws' import type { RawData } from 'ws' import type { RelayConfig } from './config.js' import type { RelayAssignmentStore } from './assignment-store.js' +import { ControlRenewalBatch } from './control-renewal-batch.js' import { RelayCredentialStore, type CredentialReservation } from './credential-store.js' import { HostCloseReasonMemory } from './host-close-reason-memory.js' import { relayHostLogDigest } from './relay-host-log-digest.js' @@ -303,6 +304,15 @@ export class HostSessionRegistry { private readonly cellIncarnation?: string ) {} + // Renewals leave the heartbeat as an enqueue: one statement per cell per + // window replaces one write transaction per host, which is what keeps the + // shared PostgreSQL instance out of buffer-header contention. + private readonly controlRenewals = new ControlRenewalBatch( + async (rows) => await this.assignments.renewControlActivities(rows), + () => this.logIdentity(), + (flush) => this.observer.recordControlRenewalFlush?.(flush) + ) + // Uniform over [CONTROL_LEASE_MS - jitter, CONTROL_LEASE_MS + jitter). private controlLeaseExpiresAt(): number { const offset = Math.floor((this.random() * 2 - 1) * CONTROL_LEASE_JITTER_MS) @@ -1384,15 +1394,13 @@ export class HostSessionRegistry { session.controlActivityId === controlActivityId && session.authorityRevision === authorityRevision && attempt > session.activityRenewalCompletedAttempt - void this.assignments - .renewControlActivity( - { userId: session.identity.sub, relayHostId: session.relayHostId }, - { - activityId: controlActivityId, - cellId: this.config.cellId, - expiresAt: startedAt + CONTROL_ACTIVITY_LEASE_MS - } - ) + void this.controlRenewals + .enqueue({ + identity: { userId: session.identity.sub, relayHostId: session.relayHostId }, + activityId: controlActivityId, + cellId: this.config.cellId, + expiresAt: startedAt + CONTROL_ACTIVITY_LEASE_MS + }) .then(() => { if (!current()) return session.activityRenewalCompletedAttempt = attempt @@ -1458,6 +1466,13 @@ export class HostSessionRegistry { session.socket?.close(RELAY_CLOSE_CODE.DRAINING, 'control activity moved') return } + if (error instanceof Error && error.message === 'assignment_lock_unavailable') { + // A per-host transaction held the row, so the batch passed over it + // rather than making every other host in the flush wait. The next + // tick is 15s away against a 105s lease, and the flush line already + // reports the count, so this needs no line of its own. + return + } console.warn('[orca-relay] control activity renewal failed') }) // Terminal handler: a throw inside the async catch above (e.g. a diff --git a/cloud/apps/relay/src/relay-observability.ts b/cloud/apps/relay/src/relay-observability.ts index 3ae1f4d3e93..dfd15eda928 100644 --- a/cloud/apps/relay/src/relay-observability.ts +++ b/cloud/apps/relay/src/relay-observability.ts @@ -1,6 +1,7 @@ import { monitorEventLoopDelay, performance } from 'node:perf_hooks' import { RELAY_REGION_METRIC_SEGMENTS, type RelayRegion } from '@orca-cloud/relay-contract' import type { ControlRenewalOutcome } from './assignment-store.js' +import type { ControlRenewalFlush } from './control-renewal-batch.js' import type { CellInventoryHoldCounts } from './cell-inventory-hold-samples.js' import type { PostgresPoolPressureCounts } from './postgres-pool-pressure.js' import type { RelayReadinessGraceEvent, RelayReadinessObservation } from './relay-readiness.js' @@ -53,6 +54,7 @@ export interface RelayRuntimeObserver { recordReconnect(): void recordSql(durationMs: number, success: boolean): void recordControlRenewal?(durationMs: number, outcome: ControlRenewalOutcome): void + recordControlRenewalFlush?(flush: ControlRenewalFlush): void recordControlActivityRecovery?(success: boolean): void recordAssignmentAdmission?(outcome: AssignmentAdmissionOutcome): void recordAssignmentRejectionReason?(lane: AssignmentAdmissionLane, reason: string): void @@ -119,6 +121,8 @@ type RelayMetricDeltas = { controlRttObserved: number controlRenewalLatenciesMs: number[] controlRenewalsByOutcome: Record + controlRenewalFlushLatenciesMs: number[] + controlRenewalFlushRowsMax: number controlActivityRecoveries: number controlActivityRecoveryFailures: number } @@ -164,6 +168,8 @@ const emptyDeltas = (): RelayMetricDeltas => ({ controlRttObserved: 0, controlRenewalLatenciesMs: [], controlRenewalsByOutcome: {}, + controlRenewalFlushLatenciesMs: [], + controlRenewalFlushRowsMax: 0, controlActivityRecoveries: 0, controlActivityRecoveryFailures: 0 }) @@ -274,6 +280,14 @@ export class RelayObservability implements RelayRuntimeObserver { (this.deltas.controlRenewalsByOutcome[outcome] ?? 0) + 1 } + recordControlRenewalFlush(flush: ControlRenewalFlush): void { + this.deltas.controlRenewalFlushLatenciesMs.push(flush.durationMs) + this.deltas.controlRenewalFlushRowsMax = Math.max( + this.deltas.controlRenewalFlushRowsMax, + flush.rows + ) + } + recordControlActivityRecovery(success: boolean): void { if (success) this.deltas.controlActivityRecoveries++ else this.deltas.controlActivityRecoveryFailures++ @@ -379,6 +393,7 @@ export class RelayObservability implements RelayRuntimeObserver { roundMs(percentile(deltas.clientAcceptStageSamplesMs[stage], 0.95)) const controlRtt = latencySummary(deltas.controlRttSamplesMs) const controlRenewal = latencySummary(deltas.controlRenewalLatenciesMs) + const controlRenewalFlush = latencySummary(deltas.controlRenewalFlushLatenciesMs) const memory = process.memoryUsage() const p99 = this.eventLoop.count === 0 ? 0 : this.eventLoop.percentile(99) / 1_000_000 this.eventLoop.reset() @@ -447,9 +462,16 @@ export class RelayObservability implements RelayRuntimeObserver { deltas.controlRenewalsByOutcome.control_activity_not_found ?? 0, controlActivityRecoveriesDelta: deltas.controlActivityRecoveries, controlActivityRecoveryFailuresDelta: deltas.controlActivityRecoveryFailures, + // Meaning changed when renewals began batching: for a batched row this is + // the flush's duration, not that row's own statement latency. The + // per-flush fields below are the ones to read for statement cost. controlRenewalLatencyMsP50: controlRenewal.p50, controlRenewalLatencyMsP95: controlRenewal.p95, controlRenewalLatencyMsMax: controlRenewal.max, + controlRenewalFlushesDelta: deltas.controlRenewalFlushLatenciesMs.length, + controlRenewalFlushRowsMax: deltas.controlRenewalFlushRowsMax, + controlRenewalFlushLatencyMsP95: controlRenewalFlush.p95, + controlRenewalFlushLatencyMsMax: controlRenewalFlush.max, httpLatencyMsMax: roundMs(deltas.httpLatencyMsMax), heapUsedBytes: memory.heapUsed, heapTotalBytes: memory.heapTotal, diff --git a/cloud/infra/terraform/relay-observability.tf b/cloud/infra/terraform/relay-observability.tf index 6b62f3a17a5..e1951eede53 100644 --- a/cloud/infra/terraform/relay-observability.tf +++ b/cloud/infra/terraform/relay-observability.tf @@ -60,6 +60,10 @@ locals { control_renewal_latency_ms_p50 = { field = "controlRenewalLatencyMsP50", description = "Control renewal latency p50 in the interval." } control_renewal_latency_ms_p95 = { field = "controlRenewalLatencyMsP95", description = "Control renewal latency p95 in the interval." } control_renewal_latency_ms_max = { field = "controlRenewalLatencyMsMax", description = "Maximum control renewal latency in the interval." } + control_renewal_flushes = { field = "controlRenewalFlushesDelta", description = "Batched control-renewal statements issued in the interval, one per cell per flush window." } + control_renewal_flush_rows_max = { field = "controlRenewalFlushRowsMax", description = "Largest number of hosts renewed by a single statement in the interval; the row ceiling is what bounds how long one flush holds its row locks." } + control_renewal_flush_ms_p95 = { field = "controlRenewalFlushLatencyMsP95", description = "Batched control-renewal statement duration p95 in the interval. Row locks live until the statement commits, so this is the lock hold." } + control_renewal_flush_ms_max = { field = "controlRenewalFlushLatencyMsMax", description = "Maximum batched control-renewal statement duration in the interval." } control_renewals = { field = "controlRenewalsDelta", description = "Control renewal attempts in the interval." } control_renewal_successes = { field = "controlRenewalSuccessesDelta", description = "Successful control renewals in the interval." } control_renewal_lease_misses = { field = "controlRenewalLeaseMissesDelta", description = "Control renewals that found their activity lease missing." } From 754134fd67e4a6359f2f7f52e4688f3a333cba24 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 17 Sep 2026 16:30:27 -0700 Subject: [PATCH 032/168] feat(agent-launch): deliver a launch prompt from the host (#21155) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(agent-launch): deliver a launch prompt from the host `agent.launch` created the surface and then reported the caller's text as `not-delivered`, always: delivery lived in the renderer, so mobile and any other caller got an agent and no prompt. The host now commits a `submit` prompt to the structured session it just created, through the same send path `agentSession.send` runs, and reports `journaled` with the transcript row's id. Nothing is queued — the durable record that the text is owed is the journal's own submission row, which the send appends before dispatching, so a host-side copy could only disagree with it. The outbox's entry and envelope builders are reused so this send is shaped exactly like a client's, fingerprint included. Everything else under-claims as `not-delivered`: a terminal's paste is observed by whoever owns the pane, a `draft` has no host-side home, and a refused or thrown send commits nothing. There is no fourth "maybe" arm — a caller holding one could neither resend nor drop the text — and dispatch doubt stays on the submission row where it already lives. * fix(agent-launch): recover committed prompt after send errors --- .../agent-launch-executor.test.ts | 63 +++++++++++- .../agent-launch/agent-launch-executor.ts | 92 +++++++++++++++--- ...t-session-launch-send-after-create.test.ts | 96 +++++++++++++++++++ .../agent-launch-structured-prompt.test.ts | 96 +++++++++++++++++++ .../methods/agent-launch-structured-prompt.ts | 78 +++++++++++++++ .../rpc/methods/agent-launch-surfaces.ts | 17 +++- src/shared/structured-agent-session-outbox.ts | 23 ++++- 7 files changed, 445 insertions(+), 20 deletions(-) create mode 100644 src/main/native-chat/agent-session-wire/structured-agent-session-launch-send-after-create.test.ts create mode 100644 src/main/runtime/rpc/methods/agent-launch-structured-prompt.test.ts create mode 100644 src/main/runtime/rpc/methods/agent-launch-structured-prompt.ts diff --git a/src/main/agent-launch/agent-launch-executor.test.ts b/src/main/agent-launch/agent-launch-executor.test.ts index 7d7de2b2b59..eb71b45537d 100644 --- a/src/main/agent-launch/agent-launch-executor.test.ts +++ b/src/main/agent-launch/agent-launch-executor.test.ts @@ -28,6 +28,7 @@ function harness(options: { createSupport?: { supported: boolean; reason?: 'agent' | 'remote' | 'wsl' } createSupportThrows?: boolean structuredCreateError?: Error + deliveredMessageId?: string | null }) { const calls: string[] = [] const createWorktree = vi.fn( @@ -51,12 +52,16 @@ function harness(options: { if (options.structuredCreateError) { throw options.structuredCreateError } - return { sessionId: 'sess-1', handle: 'handle_structured' } + return { sessionId: 'sess-1', handle: 'handle_structured', fence: 4 } }) const createTerminalAgent = vi.fn(async () => { calls.push('createTerminalAgent') return { handle: 'term_1' } }) + const deliverStructuredPrompt = vi.fn(async () => { + calls.push('deliverStructuredPrompt') + return options.deliveredMessageId === undefined ? 'msg-1' : options.deliveredMessageId + }) const runtime = { getClientSettings: () => options.settings === undefined ? STRUCTURED_PREFERENCE : options.settings, @@ -67,12 +72,13 @@ function harness(options: { createWorktree, createStructuredSession, createTerminalAgent, + deliverStructuredPrompt, run: (intent: AgentLaunchIntent) => executeAgentLaunch({ // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the stub implements only the two runtime methods the executor reaches, and each test asserts the calls made, so an omitted method throws rather than reading a wrong value. runtime: runtime as unknown as AgentLaunchExecution['runtime'], intent, - surfaces: { createStructuredSession, createTerminalAgent }, + surfaces: { createStructuredSession, createTerminalAgent, deliverStructuredPrompt }, workspaces: { createWorktree } }) } @@ -233,14 +239,63 @@ describe('an agent with no structured session', () => { }) describe('the prompt receipt', () => { - it('reports a requested prompt as not delivered rather than omitting it', async () => { + const SUBMIT = { text: 'do the thing', delivery: 'submit' } as const + + it('commits a submitted prompt to the session the launch created and names the row', async () => { + const h = harness({}) + const result = await h.run({ ...CREATE_INTENT, prompt: SUBMIT }) + + expect(result.prompt).toEqual({ delivery: 'submit', outcome: 'journaled', messageId: 'msg-1' }) + // Delivery is sequenced after the surface exists; there is nothing to send into before that. + expect(h.calls).toEqual([ + 'createWorktree(startupAgent=undefined)', + 'createSupport', + 'createStructuredSession', + 'deliverStructuredPrompt' + ]) + // The send must name the lease the create was admitted under, not one re-read later. + expect(h.deliverStructuredPrompt).toHaveBeenCalledWith({ + sessionId: 'sess-1', + fence: 4, + prompt: SUBMIT + }) + }) + + it('under-claims as not delivered when nothing was committed', async () => { + const h = harness({ deliveredMessageId: null }) + const result = await h.run({ ...CREATE_INTENT, prompt: SUBMIT }) + // A resend costs a duplicate; claiming a row that does not exist loses the text silently. + expect(result.prompt).toEqual({ delivery: 'submit', outcome: 'not-delivered' }) + }) + + it('leaves a draft with the caller, because the host has no composer to hold one', async () => { const h = harness({}) const result = await h.run({ ...CREATE_INTENT, prompt: { text: 'do the thing', delivery: 'draft' } }) - // The executor delivers nothing, so the only honest outcome is the one that under-claims. expect(result.prompt).toEqual({ delivery: 'draft', outcome: 'not-delivered' }) + expect(h.deliverStructuredPrompt).not.toHaveBeenCalled() + }) + + it('leaves a terminal launch to the pane owner', async () => { + const h = harness({ createSupport: { supported: false, reason: 'wsl' } }) + const result = await h.run({ ...CREATE_INTENT, prompt: SUBMIT }) + expect(result.outcome.kind).toBe('terminal') + expect(result.prompt).toEqual({ delivery: 'submit', outcome: 'not-delivered' }) + expect(h.deliverStructuredPrompt).not.toHaveBeenCalled() + }) + + it('leaves a reused terminal to the pane owner', async () => { + const h = harness({}) + const result = await h.run({ + agent: 'claude', + target: { kind: 'existing', worktree: 'wt-7' }, + reuseTerminal: { handle: 'term_existing' }, + prompt: SUBMIT + }) + expect(result.prompt).toEqual({ delivery: 'submit', outcome: 'not-delivered' }) + expect(h.deliverStructuredPrompt).not.toHaveBeenCalled() }) it('omits the receipt when no prompt was requested', async () => { diff --git a/src/main/agent-launch/agent-launch-executor.ts b/src/main/agent-launch/agent-launch-executor.ts index a92874b3505..91cf75c0fad 100644 --- a/src/main/agent-launch/agent-launch-executor.ts +++ b/src/main/agent-launch/agent-launch-executor.ts @@ -27,6 +27,7 @@ import type { AgentLaunchIntent, + AgentLaunchPrompt, AgentLaunchResult, AgentLaunchTarget } from '../../shared/agent-launch-intent' @@ -55,12 +56,32 @@ export type AgentLaunchSurfaceFactory = { worktreeId: string agent: 'claude' | 'codex' options?: Readonly> - }): Promise<{ sessionId: string; handle: string }> + }): Promise createTerminalAgent(args: { worktreeId: string agent: TuiAgent options?: Readonly> }): Promise<{ handle: string; warning?: string }> + /** + * Commits the launch text as the session's first turn, answering with the transcript row's id. + * + * `null` means nothing was committed, and is the answer for every failure — a refused send, an + * unreachable host, a throw. Delivery must not fail a launch whose agent is already running: the + * caller can resend under `not-delivered`, but it cannot un-create a workspace. + */ + deliverStructuredPrompt?(args: { + sessionId: string + fence: number + prompt: AgentLaunchPrompt + }): Promise +} + +/** `fence` is carried out of the create because a send must name the lease it was admitted against, + * and re-reading it later would read whatever fence the session has by then. */ +export type AgentLaunchStructuredSurface = { + sessionId: string + handle: string + fence: number } /** A structured create refusal that proves no session was committed, so the launch may downgrade. */ @@ -124,7 +145,7 @@ export async function executeAgentLaunch( outcome: { kind: 'terminal', handle: intent.reuseTerminal.handle }, worktreeId: existingWorktreeId(intent.target), receipt: preflight, - ...promptReceipt(intent) + ...promptReceipt(intent, null) } } @@ -136,7 +157,7 @@ export async function executeAgentLaunch( worktreeId: placed.worktreeId, receipt: preflight, ...(placed.warning ? { warning: placed.warning } : {}), - ...promptReceipt(intent) + ...promptReceipt(intent, null) } } @@ -150,7 +171,7 @@ export async function executeAgentLaunch( ) execution.onStage?.('surface_create') - let created: { outcome: AgentLaunchResult['outcome']; warning?: string } + let created: CreatedSurface try { created = await createSurface(execution, placed.worktreeId, settled) } catch (error) { @@ -192,10 +213,32 @@ export async function executeAgentLaunch( worktreeId: placed.worktreeId, receipt: settled, ...(warning ? { warning } : {}), - ...promptReceipt(intent) + ...promptReceipt(intent, await deliverLaunchPrompt(execution, created.structured)) } } +/** + * Hands the launch text to the surface that can commit it, which is a structured session and only + * a structured session: a terminal's paste is observed by whoever owns the pane, and a `draft` has + * no host-side home — the composer holds one, and the host has no composer. + */ +async function deliverLaunchPrompt( + execution: AgentLaunchExecution, + structured: AgentLaunchStructuredSurface | undefined +): Promise { + const { intent, surfaces } = execution + if (!intent.prompt || intent.prompt.delivery !== 'submit' || !structured) { + return null + } + return ( + (await surfaces.deliverStructuredPrompt?.({ + sessionId: structured.sessionId, + fence: structured.fence, + prompt: intent.prompt + })) ?? null + ) +} + function downgradeAgentLaunchModeForStructuredRefusal( receipt: AgentLaunchModeReceipt, vocabulary: AgentLaunchModeVocabulary @@ -234,11 +277,19 @@ async function resolveWorkspace( }) } +/** `structured` is the same surface `outcome` names, kept typed so prompt delivery reads the create's + * own fence rather than branching on `outcome.kind` and re-deriving it. */ +type CreatedSurface = { + outcome: AgentLaunchResult['outcome'] + warning?: string + structured?: AgentLaunchStructuredSurface +} + async function createSurface( execution: AgentLaunchExecution, worktreeId: string, settled: AgentLaunchModeReceipt -): Promise<{ outcome: AgentLaunchResult['outcome']; warning?: string }> { +): Promise { const { intent, surfaces } = execution if (settled.mode === 'structured' && isStructuredProvider(intent.agent)) { const session = await surfaces.createStructuredSession({ @@ -246,7 +297,10 @@ async function createSurface( agent: intent.agent, ...(intent.sessionOptions ? { options: intent.sessionOptions } : {}) }) - return { outcome: { kind: 'structured', sessionId: session.sessionId, handle: session.handle } } + return { + outcome: { kind: 'structured', sessionId: session.sessionId, handle: session.handle }, + structured: session + } } const terminal = await surfaces.createTerminalAgent({ worktreeId, @@ -293,12 +347,26 @@ function launchWorkspaceKind(target: AgentLaunchTarget): WorkspaceLaunchKind { return target.kind === 'existing' ? workspaceKindForWorktreeId(target.worktree) : 'git-worktree' } -/** Prompt delivery is the caller's, not the executor's: a PTY paste is observed by whoever owns - * the pane, and a structured first turn is sent through the session. The executor reports the - * requested delivery back as not delivered so a caller cannot mistake silence for delivery. */ -function promptReceipt(intent: AgentLaunchIntent): Pick { +/** + * The one place a disposal is constructed, so the three arms cannot drift apart. + * + * `journaled` is reachable only from a committed message id, and that id exists only because the + * host appended the transcript row first — the receipt is a consequence of the commit, never a + * write-ahead of it. Everything else under-claims as `not-delivered`, which costs a resend; there + * is deliberately no arm for "maybe", because a caller holding one could neither resend nor drop + * the text. Dispatch doubt is not this tier's to report: the submission row carries it. + */ +function promptReceipt( + intent: AgentLaunchIntent, + messageId: string | null +): Pick { if (!intent.prompt) { return {} } - return { prompt: { delivery: intent.prompt.delivery, outcome: 'not-delivered' } } + const delivery = intent.prompt.delivery + return { + prompt: messageId + ? { delivery, outcome: 'journaled', messageId } + : { delivery, outcome: 'not-delivered' } + } } diff --git a/src/main/native-chat/agent-session-wire/structured-agent-session-launch-send-after-create.test.ts b/src/main/native-chat/agent-session-wire/structured-agent-session-launch-send-after-create.test.ts new file mode 100644 index 00000000000..300f1423cb3 --- /dev/null +++ b/src/main/native-chat/agent-session-wire/structured-agent-session-launch-send-after-create.test.ts @@ -0,0 +1,96 @@ +// Where a launch prompt sent the instant `attach` resolves reaches the adapter. +// +// Both shipped adapters look the session up in a live map and throw when it is absent +// (`claude-structured-session-adapter.ts:331-337`, `codex-structured-session-state.ts`'s +// `requireLiveCodexSession`), and both populate that map as the last step of `acquire` +// (`claude-structured-session-acquisition.ts:278`, `codex-structured-session-acquire.ts:275`). +// So the question is purely one of ordering, and that is what these model. + +import { beforeEach, describe, expect, it, vi } from 'vitest' +import type { StructuredAgentSessionHost } from './structured-agent-session-host' +import { commitStructuredAgentSessionLaunchPrompt } from '../../runtime/rpc/methods/agent-launch-structured-prompt' +import { + accepted, + attachParams, + CALLER, + hostTestState +} from './structured-agent-session-host-test-harness' +import { HOST_TEST_NOW as NOW } from './structured-agent-session-host-test-data' + +let host: StructuredAgentSessionHost + +/** Mirrors both adapters: `acquire` publishes into the live map, `dispatch` throws without it. */ +function modelAdapterLiveness(registerOnAcquire: boolean): void { + const { acquire, dispatch } = hostTestState() + const live = new Set() + const spawn = acquire.getMockImplementation()! + acquire.mockImplementation(async (input) => { + const acquisition = await spawn(input) + if (registerOnAcquire) { + live.add(input.identity.sessionId) + } + return acquisition + }) + dispatch.mockImplementation(async ({ sessionId }) => { + if (!live.has(sessionId)) { + throw new Error(`no live structured session for ${sessionId}`) + } + return accepted() + }) +} + +/** Exactly what `agentLaunchSurfaceFactory` does: create, then send on the create's own fence. */ +async function launchAndDeliver(): Promise<{ + messageId: string | null + dispatchState: string +}> { + const send = vi.spyOn(host, 'send') + const created = await host.attach(CALLER, attachParams()) + if (!created.ok) { + throw new Error(`expected a create, got ${created.refusal.code}`) + } + const messageId = await commitStructuredAgentSessionLaunchPrompt({ + host, + caller: CALLER, + sessionId: created.value.sessionId, + fence: created.value.fence, + text: 'fix the failing test' + }) + const sent = await send.mock.results[0]!.value + return { + messageId, + dispatchState: sent.ok + ? sent.value.submission.dispatchState + : `refused:${sent.refusal.code}:${sent.refusal.message}` + } +} + +beforeEach(() => { + ;({ host } = hostTestState()) + // The harness pins the host clock; operation ids are minted from `Date.now()` and carry a + // timestamp the host expires against, so the two must agree or every send reads as stale. + vi.spyOn(Date, 'now').mockReturnValue(NOW) +}) + +describe('a launch prompt sent the instant the create resolves', () => { + it('reaches a live provider, because attach returns only after acquire published it', async () => { + modelAdapterLiveness(true) + + await expect(launchAndDeliver()).resolves.toEqual({ + messageId: expect.any(String), + dispatchState: 'accepted' + }) + expect(hostTestState().dispatch).toHaveBeenCalledTimes(1) + }) + + // Positive control: the assertion above is only evidence if this arm can fail, and it names + // the user-visible symptom precisely — a committed row that no agent will ever answer. + it('would commit a row and strand it if acquire ever resolved before publication', async () => { + modelAdapterLiveness(false) + + await expect(launchAndDeliver()).resolves.toEqual({ + messageId: expect.any(String), + dispatchState: 'unknown' + }) + }) +}) diff --git a/src/main/runtime/rpc/methods/agent-launch-structured-prompt.test.ts b/src/main/runtime/rpc/methods/agent-launch-structured-prompt.test.ts new file mode 100644 index 00000000000..4d919df556e --- /dev/null +++ b/src/main/runtime/rpc/methods/agent-launch-structured-prompt.test.ts @@ -0,0 +1,96 @@ +/** + * The commit half of launch-prompt delivery: what the host sends, and what it is willing to claim. + * + * The assertions that matter are that the send is shaped like every other client's — the entry's + * operation id IS the client message id, and the fingerprint is computed over the same body — and + * that no failure mode can return an id, because an id is what the caller reads as "committed". + */ + +import { describe, expect, it, vi } from 'vitest' +import { commitStructuredAgentSessionLaunchPrompt } from './agent-launch-structured-prompt' +import { structuredAgentSessionPayloadFingerprint } from '../../../../shared/structured-agent-session-mutation' +import { structuredAgentSessionSendBody } from '../../../../shared/structured-agent-session-outbox' +import type { StructuredAgentSessionHost } from '../../../native-chat/agent-session-wire/structured-agent-session-host' + +const CALLER = { callerKey: 'trusted-local:runtime' } + +function hostWith( + send: ReturnType, + journalSnapshot: ReturnType = vi.fn(() => ({ submissions: [] })) +): StructuredAgentSessionHost { + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the stub implements the only method this module reaches; any other would throw rather than read a wrong value. + return { send, journalSnapshot } as unknown as StructuredAgentSessionHost +} + +function commit(host: StructuredAgentSessionHost | null, text = 'do the thing') { + return commitStructuredAgentSessionLaunchPrompt({ + host, + caller: CALLER, + sessionId: 'sess-1', + fence: 4, + text + }) +} + +describe('committing a launch prompt', () => { + it('sends the entry as its own client message id and names the committed row', async () => { + const send = vi.fn(async (_caller, params) => ({ + ok: true as const, + value: { clientMessageId: params.envelope.clientOperationId, submission: {} } + })) + + const messageId = await commit(hostWith(send)) + + const [caller, params] = send.mock.calls[0] + expect(caller).toEqual(CALLER) + expect(messageId).toBe(params.envelope.clientOperationId) + expect(params.envelope).toMatchObject({ sessionId: 'sess-1', expectedRuntimeFence: 4 }) + expect(params.body).toEqual(structuredAgentSessionSendBody('do the thing', [])) + // The host recomputes and compares this, so a launch send must fingerprint like a client send. + expect(params.envelope.payloadFingerprint).toBe( + structuredAgentSessionPayloadFingerprint({ + method: 'agentSession.send', + sessionId: 'sess-1', + fields: { body: params.body } + }) + ) + }) + + it('claims nothing when the send is refused', async () => { + const send = vi.fn(async () => ({ + ok: false as const, + refusal: { code: 'agent_session_operation_invalid', message: 'no' } + })) + await expect(commit(hostWith(send))).resolves.toBeNull() + }) + + it('recovers a committed row when settlement throws after append', async () => { + vi.spyOn(console, 'warn').mockImplementation(() => {}) + let clientMessageId = '' + const send = vi.fn( + async (_caller: unknown, params: { envelope: { clientOperationId: string } }) => { + clientMessageId = params.envelope.clientOperationId + throw new Error('host gone') + } + ) + const journalSnapshot = vi.fn((_sessionId) => ({ + submissions: [{ clientMessageId }] + })) + await expect(commit(hostWith(send, journalSnapshot))).resolves.toEqual(clientMessageId) + }) + + it('claims nothing, and does not fail the launch, when no row was committed', async () => { + vi.spyOn(console, 'warn').mockImplementation(() => {}) + const send = vi.fn(async () => { + throw new Error('host gone') + }) + await expect(commit(hostWith(send))).resolves.toBeNull() + }) + + it('sends nothing when there is no host or no text', async () => { + const send = vi.fn() + await expect(commit(null)).resolves.toBeNull() + await expect(commit(hostWith(send), ' ')).resolves.toBeNull() + expect(send).not.toHaveBeenCalled() + }) +}) diff --git a/src/main/runtime/rpc/methods/agent-launch-structured-prompt.ts b/src/main/runtime/rpc/methods/agent-launch-structured-prompt.ts new file mode 100644 index 00000000000..0037d6fc35e --- /dev/null +++ b/src/main/runtime/rpc/methods/agent-launch-structured-prompt.ts @@ -0,0 +1,78 @@ +/** + * Host-side delivery of a launch's initial text to the structured session the launch just created. + * + * It exists so a caller does not have to implement delivery itself. Before this, `agent.launch` + * created the surface and reported the text as undelivered, which was only workable while the one + * surface that could send — the desktop renderer's chat — was also the one issuing the launch. + * Mobile and anything else calling `agent.launch` got an agent and no prompt. + * + * Nothing here queues. The durable record that the text is owed already exists and is the journal's + * own submission row: `performSend` appends it before dispatching and the attach path settles it, so + * a second host-side copy could only disagree with it. What IS reused is the outbox's entry and + * envelope builders, so this send is shaped exactly like the renderer's and mobile's — same body, + * same operation id as client message id, same payload fingerprint. + * + * The renderer still delivers its own launch prompts, because its launcher does not call + * `agent.launch` yet; when it does, its launch-sourced outbox entries become this call. + */ + +import { + createStructuredAgentSessionOutboxEntry, + structuredAgentSessionSendMutation +} from '../../../../shared/structured-agent-session-outbox' +import { createStructuredAgentSessionOperationId } from '../../../../shared/structured-agent-session-mutation' +import { randomUUID } from 'node:crypto' +import type { StructuredAgentSessionHost } from '../../../native-chat/agent-session-wire/structured-agent-session-host' +import type { StructuredAgentSessionCaller } from '../../../native-chat/agent-session-wire/structured-agent-session-host-types' + +/** + * The committed transcript row's id, or `null` when nothing was committed. + * + * `ok` is the host's own proof of the commit. A send can still throw after appending (for example + * when operation settlement fails), so throws are reconciled against the host's journal before we + * under-claim. A resend after that boundary would duplicate the model turn. + * + * Deliberately does NOT wait for the dispatch to settle. The row is committed either way, and + * whether the provider took the turn is the submission's own state to carry. + */ +export async function commitStructuredAgentSessionLaunchPrompt(args: { + host: StructuredAgentSessionHost | null + caller: StructuredAgentSessionCaller + sessionId: string + fence: number + text: string +}): Promise { + if (!args.host || args.text.trim().length === 0) { + return null + } + const clientMessageId = createStructuredAgentSessionOperationId(randomUUID) + const entry = createStructuredAgentSessionOutboxEntry({ + clientMessageId, + sessionId: args.sessionId, + text: args.text, + attachments: [], + queuedAt: Date.now() + }) + try { + const result = await args.host.send( + args.caller, + structuredAgentSessionSendMutation(entry, args.fence) + ) + return result.ok ? result.value.clientMessageId : null + } catch (error) { + // Settlement can fail after the journal append. Re-read the authoritative row before asking + // the caller to resend, otherwise a retry creates a duplicate turn. + try { + const committed = args.host + .journalSnapshot(args.sessionId) + .submissions.find((submission) => submission.clientMessageId === clientMessageId) + if (committed) { + return clientMessageId + } + } catch { + // The host may have gone away before the snapshot; the caller retains the text in that case. + } + console.warn('[agent-launch] the session was created, its launch prompt was not sent', error) + return null + } +} diff --git a/src/main/runtime/rpc/methods/agent-launch-surfaces.ts b/src/main/runtime/rpc/methods/agent-launch-surfaces.ts index ad5cb7c4c86..e610da8285d 100644 --- a/src/main/runtime/rpc/methods/agent-launch-surfaces.ts +++ b/src/main/runtime/rpc/methods/agent-launch-surfaces.ts @@ -6,6 +6,9 @@ * new agent tab is. Orchestration's own factories are NOT reusable here — a worker's session * carries a dispatch hold, a mailbox and a background tab that a launch the user asked for must * not take — which is why the executor injects this rather than branching. + * + * Delivering the launch text is here for the same reason: it is the wire-shaped half, and only the + * structured half has somewhere to commit it to. */ import { randomUUID } from 'node:crypto' @@ -21,6 +24,7 @@ import type { StructuredAgentSessionHost } from '../../../native-chat/agent-sess import type { RpcContext } from '../core' import { structuredCallerFor } from './structured-agent-session-gate' import { createStructuredAgentSessionForWorktree } from './structured-agent-session-create' +import { commitStructuredAgentSessionLaunchPrompt } from './agent-launch-structured-prompt' /** Replay-safe launches keep the nested attach in the same stable caller namespace as the launch. */ export function agentLaunchSurfaceFactory( @@ -65,9 +69,20 @@ export function agentLaunchSurfaceFactory( } return { sessionId: created.value.sessionId, - handle: structuredAgentSessionTabId(created.value.sessionId) + handle: structuredAgentSessionTabId(created.value.sessionId), + fence: created.value.fence } }, + deliverStructuredPrompt: async ({ sessionId, fence, prompt }) => + commitStructuredAgentSessionLaunchPrompt({ + host: getStructuredAgentSessionHost(), + caller: operationCallerKey + ? { callerKey: operationCallerKey } + : structuredCallerFor(context), + sessionId, + fence, + text: prompt.text + }), createTerminalAgent: async ({ worktreeId, agent }) => { const terminal = await context.runtime.createTerminal(`id:${worktreeId}`, { // The agent id is not a shell command — `cursor` is the desktop app, its CLI is diff --git a/src/shared/structured-agent-session-outbox.ts b/src/shared/structured-agent-session-outbox.ts index 56fb633dada..b2855ac1021 100644 --- a/src/shared/structured-agent-session-outbox.ts +++ b/src/shared/structured-agent-session-outbox.ts @@ -1,6 +1,9 @@ import type { AgentJournalMessageItem, AgentJournalSubmission } from './agent-session-journal-types' import { agentSessionRefusalOperationState } from './agent-session-refusal-retry' -import type { AgentSessionWireRefusalCode } from './agent-session-wire' +import type { + AgentSessionMutationEnvelope, + AgentSessionWireRefusalCode +} from './agent-session-wire' import { structuredAgentSessionPayloadFingerprint } from './structured-agent-session-mutation' import { DISPATCH_REJECTED_CANCELLED } from './structured-agent-session-dispatch-rejection' @@ -193,10 +196,17 @@ export function parseStructuredAgentSessionOutboxEntry( } } -export function structuredAgentSessionSendRequest( +export type StructuredAgentSessionSendMutation = { + envelope: AgentSessionMutationEnvelope + body: AgentJournalMessageItem +} + +/** The `agentSession.send` arguments an entry stands for. Typed rather than wire-shaped so a host + * calling its own send path builds the same envelope a client would, fingerprint included. */ +export function structuredAgentSessionSendMutation( entry: StructuredAgentSessionOutboxEntry, expectedRuntimeFence: number -): Record { +): StructuredAgentSessionSendMutation { const fields = { body: entry.body } return { envelope: { @@ -213,6 +223,13 @@ export function structuredAgentSessionSendRequest( } } +export function structuredAgentSessionSendRequest( + entry: StructuredAgentSessionOutboxEntry, + expectedRuntimeFence: number +): Record { + return structuredAgentSessionSendMutation(entry, expectedRuntimeFence) +} + export type StructuredAgentSessionSendFailure = 'delivery-unknown' | 'failed' export function classifyStructuredAgentSessionSendFailure( From 399306c17184b5c47ef30159794bd47b89ddbb49 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 20:22:07 -0400 Subject: [PATCH 033/168] feat(relay-ops): allow the migration-only cells c17 and c18 in same-cap waves (#21307) c17 and c18 hold no hosts and sit outside general admission, so rolling one displaces nobody. They are the only zero-displacement canary for a new cell image, but the same-cap wave refused them at the dispatch validator and would have promoted them to general at the end if it had not. Add them to the approved list and teach the wave a cell's entry admission class: the precheck demands the class the cell is declared to serve in, the restore hands it back that class, the isolate on an already-isolated cell is asserted to change nothing, and the selector generation advances by 2 for a general cell and by 0 for a migration-only one. One wave may not mix the two, because every cell after the first offsets from a single per-wave delta. Neither cell is a declared regional-rehome source, so its template carries no rehome trust lines. The source-membership guard now fires exactly when a roll expects those lines instead of for every US cell, which is the invariant it was standing in for, and which limits c17 and c18 to rehome protocol 0. --- ...d-deploy-relay-production-same-cap-job.yml | 89 ++++++--- .../src/incident-live-preflight-cli.test.ts | 36 ++++ .../src/incident-live-preflight-cli.ts | 21 ++- ...-relay-production-capacity-canary.test.mjs | 10 +- .../relay-production-same-cap-wave.mjs | 33 +++- .../relay-production-same-cap-wave.test.mjs | 79 ++++++++ .../relay-regional-rehome-workflow.test.mjs | 9 +- .../relay-same-cap-script-census.test.mjs | 176 +++++++++++++++++- cloud/docs/relay-workflows.md | 11 +- 9 files changed, 417 insertions(+), 47 deletions(-) diff --git a/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml b/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml index f6a577d2b42..050e47f4e8b 100644 --- a/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml +++ b/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml @@ -87,12 +87,6 @@ jobs: "SKIP_RELAY_MONITOR_GATE ${TARGET_IMAGE_DIGEST}" [[ "${GATE_OVERRIDE_REASON}" =~ ^[[:print:]]{12,500}$ ]] fi - if test "${DEPLOY_MODE}" = verify; then - EFFECTIVE_SELECTOR_GENERATION="${EXPECTED_SELECTOR_GENERATION}" - else - EFFECTIVE_SELECTOR_GENERATION="$((EXPECTED_SELECTOR_GENERATION + (2 * WAVE_INDEX)))" - fi - echo "EFFECTIVE_SELECTOR_GENERATION=${EFFECTIVE_SELECTOR_GENERATION}" >> "${GITHUB_ENV}" if test "${DEPLOY_MODE}" != verify && test "${GITHUB_RUN_ATTEMPT}" != 1; then echo "mutations are single-dispatch: re-runs replay aged evidence," >&2 echo "so recover each remaining cell with its own fresh monitor" >&2 @@ -124,6 +118,26 @@ jobs: - uses: hashicorp/setup-terraform@v3 with: { terraform_wrapper: false } + # One approved-cell table, in the wave validator the dispatch gate already uses, so + # a cell's class and its wave's selector delta cannot drift apart between the two. + - name: Resolve this cell's admission class and wave selector delta + run: | + CELL_CLASS="$(node dev/scripts/relay-production-same-cap-wave.mjs cell-class \ + --cell-id "${TARGET_CELL_ID}")" + ENTRY_ADMISSION="$(jq -er '.entryAdmission' <<< "${CELL_CLASS}")" + SELECTOR_WAVE_DELTA="$(jq -er '.selectorWaveDelta' <<< "${CELL_CLASS}")" + if test "${DEPLOY_MODE}" = verify; then + EFFECTIVE_SELECTOR_GENERATION="${EXPECTED_SELECTOR_GENERATION}" + else + EFFECTIVE_SELECTOR_GENERATION="$((EXPECTED_SELECTOR_GENERATION \ + + (SELECTOR_WAVE_DELTA * WAVE_INDEX)))" + fi + { + echo "ENTRY_ADMISSION=${ENTRY_ADMISSION}" + echo "SELECTOR_WAVE_DELTA=${SELECTOR_WAVE_DELTA}" + echo "EFFECTIVE_SELECTOR_GENERATION=${EFFECTIVE_SELECTOR_GENERATION}" + } >> "${GITHUB_ENV}" + - name: Require fresh aggregate monitor evidence reference if: ${{ inputs.mode != 'verify' && inputs.gate-override-confirmation == '' }} run: | @@ -217,11 +231,13 @@ jobs: --no-monitor-state \ --expected-selector-generation "${EXPECTED_SELECTOR_GENERATION}" \ --selector-membership-file "${RUNNER_TEMP}/relay-same-cap-selector.json" \ - --wave-index "${WAVE_INDEX}" --retry-freshness + --wave-index "${WAVE_INDEX}" \ + --selector-wave-delta "${SELECTOR_WAVE_DELTA}" --retry-freshness else pnpm incident:relay-preflight -- \ --state-file "${OUTPUT_DIRECTORY}/relay-${MONITOR_RUN_ID}-dry-run.state.json" \ - --wave-index "${WAVE_INDEX}" --retry-freshness + --wave-index "${WAVE_INDEX}" \ + --selector-wave-delta "${SELECTOR_WAVE_DELTA}" --retry-freshness fi - name: Require durable rehome disabled and exact selector @@ -251,6 +267,11 @@ jobs: EXPECTED_REGION=us-central1 EXPECTED_DATABASE_POOL_MAX= ;; + c17|c18) + EXPECTED_HARD_CAP=600 + EXPECTED_REGION=us-central1 + EXPECTED_DATABASE_POOL_MAX= + ;; c27|c28|c29) EXPECTED_HARD_CAP=3000 EXPECTED_REGION=asia-east2 @@ -266,10 +287,6 @@ jobs: SOURCE_CELLS="$(terraform -chdir=infra/terraform console \ -var-file=environments/production.tfvars \ <<< 'jsonencode(var.relay_region_rehome_source_cell_ids)' | jq -er '.')" - if test "${EXPECTED_REGION}" = us-central1; then - jq -e --arg cell "${TARGET_CELL_ID}" 'index($cell) != null' \ - <<< "${SOURCE_CELLS}" >/dev/null - fi CURRENT_SHAPE="$(jq -cer --arg cell "${TARGET_CELL_ID}" '.[$cell]' <<< "${CELLS_JSON}")" test "$(jq -r '.connection_hard_cap' <<< "${CURRENT_SHAPE}")" = "${EXPECTED_HARD_CAP}" test "$(jq -r '.connection_unobserved_bound' <<< "${CURRENT_SHAPE}")" = \ @@ -291,6 +308,14 @@ jobs: DESIRED_REHOME_PROTOCOL="${TARGET_REHOME_PROTOCOL}" CURRENT_REHOME_PROTOCOL="${ROLLBACK_REHOME_PROTOCOL}" fi + # The startup template emits rehome trust lines only for a declared source cell, so + # require membership exactly when either side of this roll expects those lines. + if test "${EXPECTED_REGION}" = us-central1 && { + test "${DESIRED_REHOME_PROTOCOL}" != 0 || test "${CURRENT_REHOME_PROTOCOL}" != 0 + }; then + jq -e --arg cell "${TARGET_CELL_ID}" 'index($cell) != null' \ + <<< "${SOURCE_CELLS}" >/dev/null + fi DESIRED_IMAGE="${IMAGE_REPOSITORY}@${DESIRED_IMAGE_DIGEST}" OVERRIDE_CELLS_JSON="$(jq -ce --arg cell "${TARGET_CELL_ID}" \ --arg image "${DESIRED_IMAGE}" '.[$cell].image = $image' <<< "${CELLS_JSON}")" @@ -369,6 +394,12 @@ jobs: '$value | split(",") | map(select(length > 0 and . != $target)) | unique | join(",")')" test -n "${ISOLATED_MIGRATION_CELLS}" || ISOLATED_MIGRATION_CELLS=none test -n "${ISOLATED_GENERAL_CELLS}" || ISOLATED_GENERAL_CELLS=none + # A migration-only cell is already isolated and is handed back isolated, so both + # halves of its wave see exactly the membership it entered with. + if test "${ENTRY_ADMISSION}" = migration-only; then + RESTORED_MIGRATION_CELLS="${ISOLATED_MIGRATION_CELLS}" + RESTORED_GENERAL_CELLS="${ISOLATED_GENERAL_CELLS}" + fi { echo "ROLLBACK_RESUME=${ROLLBACK_RESUME}" # The failsafe consumes these; deriving them here keeps them @@ -445,12 +476,13 @@ jobs: echo "SOURCE_INCARNATION=${SOURCE_INCARNATION}" >> "${GITHUB_ENV}" # Rollback is the documented recovery from a failed canary, which # leaves the cell migration-only (and possibly still marked - # draining); apply and verify still require a pristine general cell. + # draining); apply and verify still require the cell pristine in the + # class it is declared to serve in. if test "${DEPLOY_MODE}" = rollback; then PRECHECK_ADMISSION=general-or-migration-only PRECHECK_DRAINING=either else - PRECHECK_ADMISSION=general + PRECHECK_ADMISSION="${ENTRY_ADMISSION}" PRECHECK_DRAINING=forbidden fi node dev/scripts/verify-relay-capacity-transition.mjs \ @@ -478,6 +510,11 @@ jobs: --director-origin "${DIRECTOR_ORIGIN}" --cell-origin "${CELL_ORIGIN}" \ --cell-id "${TARGET_CELL_ID}" --approved-cells same-cap --mode isolate)" echo "${ISOLATE_RESULT}" + # Isolating a migration-only cell must be a read-only no-op; a change here would + # mean the live class is not the one this wave planned around. + if test "${ENTRY_ADMISSION}" = migration-only; then + jq -e '.changed == false' <<< "${ISOLATE_RESULT}" >/dev/null + fi ISOLATE_GENERATION="$(jq -er '.generation' <<< "${ISOLATE_RESULT}")" echo "SELECTOR_GENERATION_AFTER_ISOLATE=${ISOLATE_GENERATION}" >> "${GITHUB_ENV}" node dev/scripts/prepare-relay-production-capacity-canary.mjs \ @@ -664,28 +701,36 @@ jobs: --director-origin "${DIRECTOR_ORIGIN}" --cell-id "${TARGET_CELL_ID}" \ --cell-incarnation "${TARGET_INCARNATION}" - - name: Restore only the verified selected cell to general admission + - name: Restore only the verified selected cell to its entry admission if: ${{ inputs.mode != 'verify' }} env: ORCA_RELAY_ADMIN_ID_TOKEN: ${{ steps.post-auth.outputs.id_token }} run: | echo "MUTATION_STARTED=true" >> "${GITHUB_ENV}" - ACTIVATE_RESULT="$(node dev/scripts/prepare-relay-production-capacity-canary.mjs \ + # Activating a migration-only cell would promote it to a serving cell for good, so + # restore it with the idempotent isolate that reports the authoritative generation. + if test "${ENTRY_ADMISSION}" = migration-only; then + RESTORE_MODE=isolate + else + RESTORE_MODE=activate + fi + RESTORE_RESULT="$(node dev/scripts/prepare-relay-production-capacity-canary.mjs \ --director-origin "${DIRECTOR_ORIGIN}" --cell-origin "${CELL_ORIGIN}" \ - --cell-id "${TARGET_CELL_ID}" --approved-cells same-cap --mode activate)" - echo "${ACTIVATE_RESULT}" - SELECTOR_GENERATION_AFTER_ACTIVATE="$(jq -er '.generation' \ - <<< "${ACTIVATE_RESULT}")" + --cell-id "${TARGET_CELL_ID}" --approved-cells same-cap --mode "${RESTORE_MODE}")" + echo "${RESTORE_RESULT}" + SELECTOR_GENERATION_AFTER_RESTORE="$(jq -er '.generation' \ + <<< "${RESTORE_RESULT}")" node dev/scripts/verify-relay-capacity-transition.mjs \ --director-origin "${DIRECTOR_ORIGIN}" --cell-origin "${CELL_ORIGIN}" \ --cell-id "${TARGET_CELL_ID}" --hard-cap "${EXPECTED_HARD_CAP}" \ --unobserved-bound "${EXPECTED_UNOBSERVED_BOUND}" \ - --heartbeat fresh --admission general --draining forbidden --activity allowed \ + --heartbeat fresh --admission "${ENTRY_ADMISSION}" \ + --draining forbidden --activity allowed \ --expected-image-digests "${DESIRED_IMAGE_DIGEST}" \ --regional-rehome-protocol "${DESIRED_REHOME_PROTOCOL}" node dev/scripts/operate-relay-regional-rehome.mjs \ --mode inspect --director-origin "${DIRECTOR_ORIGIN}" \ - --expected-selector-generation "${SELECTOR_GENERATION_AFTER_ACTIVATE}" \ + --expected-selector-generation "${SELECTOR_GENERATION_AFTER_RESTORE}" \ --expected-existing-only-cells "${EXPECTED_EXISTING_ONLY_CELLS}" \ --expected-migration-only-cells "${RESTORED_MIGRATION_CELLS}" \ --expected-general-cells "${RESTORED_GENERAL_CELLS}" \ diff --git a/cloud/apps/relay-ops/src/incident-live-preflight-cli.test.ts b/cloud/apps/relay-ops/src/incident-live-preflight-cli.test.ts index 532cf5279eb..587143ed7e0 100644 --- a/cloud/apps/relay-ops/src/incident-live-preflight-cli.test.ts +++ b/cloud/apps/relay-ops/src/incident-live-preflight-cli.test.ts @@ -738,6 +738,42 @@ describe('relay incident live preflight', () => { expect(seen[0]!.generation).toBe(5) }) + it('offsets by the wave delta the cell class declares', async () => { + const generationFor = async (args: string[]) => { + const seen: AdmissionSelector[] = [] + await expect(runIncidentLivePreflight(args, { + now: () => now, + collect: async (expected) => { + seen.push(expected) + const next = canonicalSample(expected.generation) + next.expectedSelector = expected + return next + } + })).resolves.toBeUndefined() + return seen[0]!.generation + } + // A migration-only cell's wave isolates and restores nothing, so no predecessor moved it. + expect(await generationFor( + overrideArgs(['--wave-index', '2', '--selector-wave-delta', '0']) + )).toBe(1) + expect(await generationFor( + overrideArgs(['--wave-index', '2', '--selector-wave-delta', '2']) + )).toBe(5) + }) + + it('rejects a selector wave delta no cell class produces', async () => { + for (const delta of ['1', '3', '4', '', '-0', '02']) { + await expect(runIncidentLivePreflight( + overrideArgs(['--selector-wave-delta', delta]), + { now: () => now } + )).rejects.toThrow('usage:') + } + await expect(runIncidentLivePreflight( + overrideArgs(['--selector-wave-delta', '0', '--selector-wave-delta', '0']), + { now: () => now } + )).rejects.toThrow('usage:') + }) + it('pins the strictest migration policy', async () => { // An inactive migration target is tolerable only under recover-forward, // and an override cannot elect that policy, so this must still fail. diff --git a/cloud/apps/relay-ops/src/incident-live-preflight-cli.ts b/cloud/apps/relay-ops/src/incident-live-preflight-cli.ts index d168fe69c46..5183286e302 100644 --- a/cloud/apps/relay-ops/src/incident-live-preflight-cli.ts +++ b/cloud/apps/relay-ops/src/incident-live-preflight-cli.ts @@ -31,6 +31,8 @@ const MONITOR_EVIDENCE_MAX_AGE_MS = 10 * 60_000 // Matches the same-cap cell job timeout-minutes; bounds each predecessor wave. const WAVE_PREDECESSOR_TIMEOUT_MS = 75 * 60_000 const WAVE_INDEX_PATTERN = /^[0-3]$/ +// 2 for a general cell's isolate-and-restore wave, 0 for a migration-only cell's no-op pair. +const SELECTOR_WAVE_DELTA_PATTERN = /^[02]$/ export function livePreflightGcloud( gcloud: ReturnType, @@ -93,13 +95,16 @@ function describeFailure(failure: IncidentFailure): string { } const PREFLIGHT_USAGE = - 'usage: --state-file [--wave-index <0-3>] [--retry-freshness]' + + 'usage: --state-file [--wave-index <0-3>]' + + ' [--selector-wave-delta <0|2>] [--retry-freshness]' + ' | --no-monitor-state --expected-selector-generation ' + - ' --selector-membership-file [--wave-index <0-3>]' + ' --selector-membership-file [--wave-index <0-3>]' + + ' [--selector-wave-delta <0|2>]' const VALUE_OPTIONS = new Set([ '--state-file', '--wave-index', + '--selector-wave-delta', '--expected-selector-generation', '--selector-membership-file' ]) @@ -241,6 +246,8 @@ export async function runIncidentLivePreflight( const parsed = parsePreflightArgs(argv) const waveIndex = parsed.options.get('--wave-index') ?? '0' if (!WAVE_INDEX_PATTERN.test(waveIndex)) throw new Error(PREFLIGHT_USAGE) + const selectorWaveDelta = parsed.options.get('--selector-wave-delta') ?? '2' + if (!SELECTOR_WAVE_DELTA_PATTERN.test(selectorWaveDelta)) throw new Error(PREFLIGHT_USAGE) const now = dependencies.now ?? Date.now const plan = parsed.flags.has('--no-monitor-state') ? await overridePreflightPlan(parsed.options, now()) @@ -252,14 +259,16 @@ export async function runIncidentLivePreflight( dependencies.environment ) // Each predecessor same-cap apply wave reversibly isolates and restores its - // cell, advancing the selector generation by exactly 2 with membership - // unchanged (rollback is single-cell, so it never reaches a later wave), so - // the live selector comparison must expect the wave-adjusted generation. + // cell with membership unchanged (rollback is single-cell, so it never reaches + // a later wave), so the live selector comparison must expect the wave-adjusted + // generation. A general cell advances it by 2; a migration-only cell is already + // isolated and stays that way, so its wave advances it by 0. A wave is never + // mixed, so one delta covers every predecessor. const collectOptions = { environment: plan.environment, expectedSelector: { ...plan.expectedSelector, - generation: plan.expectedSelector.generation + 2 * Number(waveIndex) + generation: plan.expectedSelector.generation + Number(selectorWaveDelta) * Number(waveIndex) }, ...(dependencies.now ? { now: dependencies.now } : {}) } diff --git a/cloud/dev/scripts/prepare-relay-production-capacity-canary.test.mjs b/cloud/dev/scripts/prepare-relay-production-capacity-canary.test.mjs index c566229b678..e721bfb8c75 100644 --- a/cloud/dev/scripts/prepare-relay-production-capacity-canary.test.mjs +++ b/cloud/dev/scripts/prepare-relay-production-capacity-canary.test.mjs @@ -113,8 +113,12 @@ describe('production Relay capacity cell admission', () => { ]), /not approved/) }) - it('admits the same-cap Asia cells only under the same-cap allowlist', () => { - for (const cellId of ['production-gce-c27', 'production-gce-c28', 'production-gce-c29']) { + it('admits the same-cap Asia and migration-only cells only under the same-cap allowlist', () => { + for (const cellId of [ + 'production-gce-c27', 'production-gce-c28', 'production-gce-c29', + // Migration-only canaries: the US-only capacity rollout never touches them either. + 'production-gce-c17', 'production-gce-c18' + ]) { const hostname = cellId.slice('production-gce-'.length) assert.deepEqual(parseProductionCapacityCellArguments([ '--director-origin', 'https://relay.onorca.dev', @@ -130,7 +134,7 @@ describe('production Relay capacity cell admission', () => { paceWindowMs: 0 }) } - for (const cellId of ['production-gce-c17', 'production-gce-c18', 'production-gce-c30']) { + for (const cellId of ['production-gce-c12', 'production-gce-c30']) { const hostname = cellId.slice('production-gce-'.length) assert.throws(() => parseProductionCapacityCellArguments([ '--director-origin', 'https://relay.onorca.dev', diff --git a/cloud/dev/scripts/relay-production-same-cap-wave.mjs b/cloud/dev/scripts/relay-production-same-cap-wave.mjs index 4fd1c030d95..a3b6fee31b6 100644 --- a/cloud/dev/scripts/relay-production-same-cap-wave.mjs +++ b/cloud/dev/scripts/relay-production-same-cap-wave.mjs @@ -2,14 +2,29 @@ import { readFileSync } from 'node:fs' import { pathToFileURL } from 'node:url' import { requireSameEvidenceCode } from './relay-evidence-code-provenance.mjs' +// Migration-only by policy: zero hosts and no reservation, so a wave rolls one without +// displacing anybody. It enters and must leave migration-only, never general. +export const SAME_CAP_MIGRATION_ONLY_CELLS = ['production-gce-c17', 'production-gce-c18'] + export const SAME_CAP_CELLS = [ 'production-gce-c7', 'production-gce-c8', 'production-gce-c9', 'production-gce-c10', 'production-gce-c13', 'production-gce-c14', 'production-gce-c15', 'production-gce-c16', 'production-gce-c19', 'production-gce-c20', 'production-gce-c21', 'production-gce-c22', 'production-gce-c23', 'production-gce-c24', 'production-gce-c25', 'production-gce-c26', - 'production-gce-c27', 'production-gce-c28', 'production-gce-c29' + 'production-gce-c27', 'production-gce-c28', 'production-gce-c29', + ...SAME_CAP_MIGRATION_ONLY_CELLS ] +// A general cell's wave isolates and restores it, advancing the selector twice; a +// migration-only cell's isolate and restore are both no-ops, so its wave advances nothing. +export function selectorWaveDelta(cellId) { + return SAME_CAP_MIGRATION_ONLY_CELLS.includes(cellId) ? 0 : 2 +} + +export function entryAdmission(cellId) { + return SAME_CAP_MIGRATION_ONLY_CELLS.includes(cellId) ? 'migration-only' : 'general' +} + function digest(value, name) { if (!/^sha256:[a-f0-9]{64}$/.test(value ?? '')) throw new Error(`${name} is invalid`) return value @@ -23,6 +38,11 @@ function cells(value) { new Set(parsed).size !== parsed.length || parsed.some((cell) => !SAME_CAP_CELLS.includes(cell)) ) throw new Error('same-cap wave cells are invalid') + // Every later cell offsets from one per-wave selector delta, and the two classes + // have different ones, so a mixed wave has no single offset any cell could use. + if (new Set(parsed.map(selectorWaveDelta)).size > 1) { + throw new Error('same-cap wave cells must be all general or all migration-only') + } return parsed } @@ -101,7 +121,7 @@ export function canaryAuthority(input) { cellId: wave.cells[0], targetDigest: wave.targetDigest, rollbackDigest: wave.rollbackDigest, - selectorGeneration: selectorGeneration + 2, + selectorGeneration: selectorGeneration + selectorWaveDelta(wave.cells[0]), rehomeGeneration, // Audit trail, not authority: a batch reusing this canary is authorized by // its own confirmation, so verification below neither requires nor forbids it. @@ -182,6 +202,15 @@ export function main(argv = process.argv.slice(2)) { }))}\n`) return } + if (command === 'cell-class') { + const cellId = input['cell-id'] + if (!SAME_CAP_CELLS.includes(cellId)) throw new Error('same-cap wave cells are invalid') + process.stdout.write(`${JSON.stringify({ + entryAdmission: entryAdmission(cellId), + selectorWaveDelta: selectorWaveDelta(cellId) + })}\n`) + return + } if (command === 'verify-canary') { verifyCanaryAuthority(JSON.parse(readFileSync(input.file, 'utf8')), { commitSha: input['commit-sha'], diff --git a/cloud/dev/scripts/relay-production-same-cap-wave.test.mjs b/cloud/dev/scripts/relay-production-same-cap-wave.test.mjs index 8fea3ecc195..997b5975968 100644 --- a/cloud/dev/scripts/relay-production-same-cap-wave.test.mjs +++ b/cloud/dev/scripts/relay-production-same-cap-wave.test.mjs @@ -5,7 +5,11 @@ import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { test } from 'node:test' import { + SAME_CAP_CELLS, + SAME_CAP_MIGRATION_ONLY_CELLS, canaryAuthority, + entryAdmission, + main, validateSameCapWave, verifyCanaryAuthority } from './relay-production-same-cap-wave.mjs' @@ -52,6 +56,81 @@ test('requires one canary or a bounded reviewed batch', () => { }), /cells/) }) +test('rolls the migration-only cells but never mixes the two classes in one wave', () => { + for (const cellId of SAME_CAP_MIGRATION_ONLY_CELLS) { + assert.equal(SAME_CAP_CELLS.includes(cellId), true, cellId) + assert.equal(entryAdmission(cellId), 'migration-only', cellId) + assert.deepEqual(validateSameCapWave({ + mode: 'canary-apply', + cellIds: cellId, + targetDigest, + rollbackDigest, + confirmation: `ROLL_RELAY_SAME_CAP ${targetDigest} ${cellId}` + }).cells, [cellId]) + } + const cellIds = 'production-gce-c17,production-gce-c18' + assert.deepEqual(validateSameCapWave({ + mode: 'batch-apply', + cellIds, + targetDigest, + rollbackDigest, + confirmation: `ROLL_RELAY_SAME_CAP ${targetDigest} ${cellIds}`, + canaryRunId: '42' + }).cells, ['production-gce-c17', 'production-gce-c18']) + // A mixed wave has no single selector delta for its later cells to offset from. + const mixed = 'production-gce-c7,production-gce-c17' + assert.throws(() => validateSameCapWave({ + mode: 'batch-apply', + cellIds: mixed, + targetDigest, + rollbackDigest, + confirmation: `ROLL_RELAY_SAME_CAP ${targetDigest} ${mixed}`, + canaryRunId: '42' + }), /all general or all migration-only/) +}) + +test('seals a migration-only canary at the generation its wave leaves behind', () => { + const seal = (cellId) => canaryAuthority({ + cellIds: cellId, + targetDigest, + rollbackDigest, + confirmation: `ROLL_RELAY_SAME_CAP ${targetDigest} ${cellId}`, + commitSha: 'c'.repeat(40), + runId: '42', + selectorGeneration: '11', + rehomeGeneration: '4' + }) + // Isolate and restore are both no-ops on a migration-only cell, so nothing advances. + assert.equal(seal('production-gce-c17').selectorGeneration, 11) + assert.equal(seal('production-gce-c7').selectorGeneration, 13) + // That canary still authorizes a later general batch; it is evidence about the image. + assert.equal(verifyCanaryAuthority(seal('production-gce-c17'), { + commitSha: 'c'.repeat(40), + runId: '42', + targetDigest, + rollbackDigest, + selectorGeneration: '11', + rehomeGeneration: '4' + }).cellId, 'production-gce-c17') +}) + +test('reports each approved cell\'s class and selector delta', () => { + const printed = [] + const write = process.stdout.write.bind(process.stdout) + process.stdout.write = (chunk) => printed.push(String(chunk)) + try { + main(['cell-class', '--cell-id', 'production-gce-c17']) + main(['cell-class', '--cell-id', 'production-gce-c7']) + } finally { + process.stdout.write = write + } + assert.deepEqual(printed.map((line) => JSON.parse(line)), [ + { entryAdmission: 'migration-only', selectorWaveDelta: 0 }, + { entryAdmission: 'general', selectorWaveDelta: 2 } + ]) + assert.throws(() => main(['cell-class', '--cell-id', 'production-gce-c12']), /cells are invalid/) +}) + test('binds rollback confirmation to the exact digest and ordered cells', () => { assert.throws(() => validateSameCapWave({ mode: 'rollback', diff --git a/cloud/dev/scripts/relay-regional-rehome-workflow.test.mjs b/cloud/dev/scripts/relay-regional-rehome-workflow.test.mjs index 0168581fd58..829005df7fc 100644 --- a/cloud/dev/scripts/relay-regional-rehome-workflow.test.mjs +++ b/cloud/dev/scripts/relay-regional-rehome-workflow.test.mjs @@ -87,7 +87,7 @@ test('same-cap wrapper is reusable, canary-bound, and sequential', () => { assert.match(job, /SELECTOR_GENERATION_AFTER_ISOLATE=\$\{EFFECTIVE_SELECTOR_GENERATION\}/) assert.match(job, /SELECTOR_GENERATION_AFTER_ISOLATE=\$\{ISOLATE_GENERATION\}/) assert.match(job, /--expected-selector-generation "\$\{SELECTOR_GENERATION_AFTER_ISOLATE\}"/) - assert.match(job, /--expected-selector-generation "\$\{SELECTOR_GENERATION_AFTER_ACTIVATE\}"/) + assert.match(job, /--expected-selector-generation "\$\{SELECTOR_GENERATION_AFTER_RESTORE\}"/) assert.match(job, /--expected-migration-only-cells "\$\{RESTORED_MIGRATION_CELLS\}"/) assert.match(job, /--expected-general-cells "\$\{RESTORED_GENERAL_CELLS\}"/) assert.match(job, /FAILSAFE_GENERATION/) @@ -98,14 +98,17 @@ test('same-cap wrapper is reusable, canary-bound, and sequential', () => { // Wave 0 must retry freshness-only failures too: one Cloud Monitoring publish // lag at the sample instant is not health evidence, and single-shot wave 0 // failed a whole batch on a series that was fresh again a minute later. - assert.match(job, /dry-run\.state\.json" \\\n {14}--wave-index "\$\{WAVE_INDEX\}" --retry-freshness/) + assert.match( + job, + /dry-run\.state\.json" \\\n {14}--wave-index "\$\{WAVE_INDEX\}" \\\n {14}--selector-wave-delta "\$\{SELECTOR_WAVE_DELTA\}" --retry-freshness/ + ) assert.doesNotMatch(job, /RETRY_ARGS/) // Break-glass: the override skips the aggregate 15-minute monitor evidence and // nothing else. The live per-wave recheck still runs on the override path, off // the dispatch inputs the rehome inspect below verifies against the director. assert.match( job, - /if test -n "\$\{GATE_OVERRIDE_CONFIRMATION\}"; then[\s\S]{0,700}?--no-monitor-state \\\n {14}--expected-selector-generation "\$\{EXPECTED_SELECTOR_GENERATION\}" \\\n {14}--selector-membership-file[\s\S]{0,120}?--wave-index "\$\{WAVE_INDEX\}" --retry-freshness/ + /if test -n "\$\{GATE_OVERRIDE_CONFIRMATION\}"; then[\s\S]{0,700}?--no-monitor-state \\\n {14}--expected-selector-generation "\$\{EXPECTED_SELECTOR_GENERATION\}" \\\n {14}--selector-membership-file[\s\S]{0,160}?--wave-index "\$\{WAVE_INDEX\}" \\\n {14}--selector-wave-delta "\$\{SELECTOR_WAVE_DELTA\}" --retry-freshness/ ) // The override is re-validated here, not trusted from the caller, and it is // bound to the digest this wave installs. diff --git a/cloud/dev/scripts/relay-same-cap-script-census.test.mjs b/cloud/dev/scripts/relay-same-cap-script-census.test.mjs index bd49f7600a7..f6539db2f79 100644 --- a/cloud/dev/scripts/relay-same-cap-script-census.test.mjs +++ b/cloud/dev/scripts/relay-same-cap-script-census.test.mjs @@ -3,7 +3,12 @@ import { spawnSync } from 'node:child_process' import { readFileSync } from 'node:fs' import { describe, it } from 'node:test' import { parseProductionCapacityCellArguments } from './prepare-relay-production-capacity-canary.mjs' -import { SAME_CAP_CELLS } from './relay-production-same-cap-wave.mjs' +import { + SAME_CAP_CELLS, + SAME_CAP_MIGRATION_ONLY_CELLS, + entryAdmission, + selectorWaveDelta +} from './relay-production-same-cap-wave.mjs' import { readRelayWorkflow } from './relay-repository.mjs' import { validateCapacityPlan } from './validate-relay-capacity-plan.mjs' @@ -33,10 +38,19 @@ function rehomeSourceCells() { // The job cross-checks its pinned pool against the committed map; model the same read. function tfvarsDatabasePoolMax(cellId) { + return tfvarsCellBlock(cellId).match(/database_pool_max\s*=\s*(\d+)/)?.[1] ?? '10' +} + +function tfvarsHardCap(cellId) { + const cap = /connection_hard_cap\s*=\s*(\d+)/.exec(tfvarsCellBlock(cellId))?.[1] + assert.notEqual(cap, undefined, `${cellId} has no connection_hard_cap`) + return cap +} + +function tfvarsCellBlock(cellId) { const start = production.indexOf(`"${cellId}" = {`) assert.notEqual(start, -1, `${cellId} is missing from production.tfvars`) - const block = production.slice(start, production.indexOf('\n }', start)) - return /database_pool_max\s*=\s*(\d+)/.exec(block)?.[1] ?? '10' + return production.slice(start, production.indexOf('\n }', start)) } function startupScript({ cap, image, trusted, pool }) { @@ -145,6 +159,50 @@ function cellShape(cellId) { return { cap: Number(cap), pool: pool.slice('pool='.length) || undefined } } +// The class block runs before checkout-independent work and decides the whole wave shape. +function resolveCellClass(cellId) { + return spawnSync('bash', [ + '-euo', + 'pipefail', + '-c', + `${jobBlock( + ' CELL_CLASS="$(node dev/scripts/relay-production-same-cap-wave.mjs cell-class \\', + ' SELECTOR_WAVE_DELTA="$(jq -er \'.selectorWaveDelta\' <<< "${CELL_CLASS}")"' + )}\necho "\${ENTRY_ADMISSION} \${SELECTOR_WAVE_DELTA}"` + ], { cwd: new URL('../..', import.meta.url), env: { ...process.env, TARGET_CELL_ID: cellId }, encoding: 'utf8' }) +} + +function generationBlock() { + return `${jobBlock( + ' if test "${DEPLOY_MODE}" = verify; then', + ' fi' + )}\necho "\${EFFECTIVE_SELECTOR_GENERATION}"` +} + +// The job derives both memberships in one block; run that block alone for each class. +function membership(env) { + const script = `${jobBlock( + ' RESTORED_MIGRATION_CELLS="$(jq -rn \\', + ' fi' + )}\njq -cn --arg a "\${ISOLATED_MIGRATION_CELLS}" --arg b "\${ISOLATED_GENERAL_CELLS}" \\ + --arg c "\${RESTORED_MIGRATION_CELLS}" --arg d "\${RESTORED_GENERAL_CELLS}" \\ + '{isolatedMigration:$a,isolatedGeneral:$b,restoredMigration:$c,restoredGeneral:$d}'` + const resolved = spawnSync('bash', ['-euo', 'pipefail', '-c', script], { + env: { ...process.env, ...env }, + encoding: 'utf8' + }) + assert.equal(resolved.status, 0, resolved.stderr) + return JSON.parse(resolved.stdout) +} + +function jobBlock(firstLine, lastLine) { + const start = workflow.indexOf(`${firstLine}\n`) + assert.notEqual(start, -1, `the job has no ${firstLine.trim()}`) + const end = workflow.indexOf(`\n${lastLine}\n`, start) + assert.notEqual(end, -1, `that block has no ${lastLine.trim()}`) + return workflow.slice(start, end + lastLine.length + 1).replace(/^ {10}/gm, '') +} + describe('same-cap roll scripts accept every same-cap cell', () => { it('parses every wave cell through the same-cap canary allowlist', () => { for (const cellId of SAME_CAP_CELLS) { @@ -172,12 +230,13 @@ describe('same-cap roll scripts accept every same-cap cell', () => { assert.equal(resolved.status, 0, `${cellId}: ${resolved.stderr}`) assert.match( resolved.stdout.trim(), - /^(us-central1 1000 pool=|asia-east2 3000 pool=16)$/, + /^(us-central1 1000 pool=|us-central1 600 pool=|asia-east2 3000 pool=16)$/, cellId ) assert.equal(tfvarsDatabasePoolMax(cellId), cellShape(cellId).pool ?? '10', cellId) + assert.equal(String(cellShape(cellId).cap), tfvarsHardCap(cellId), cellId) } - assert.equal(resolveCellShape('production-gce-c17').status, 1) + assert.equal(resolveCellShape('production-gce-c12').status, 1) assert.equal(resolveCellShape('production-gce-c30').status, 1) }) @@ -189,7 +248,8 @@ describe('same-cap roll scripts accept every same-cap cell', () => { const end = lines.findIndex((line) => !line.endsWith('\\')) const call = lines.slice(0, end + 1).join(' ') assert.match(call, /--approved-cells same-cap/) - assert.match(call, /--mode (isolate|drain|activate)/) + // The restore call picks its mode from the cell's entry admission class. + assert.match(call, /--mode (isolate|drain|activate|"\$\{RESTORE_MODE\}")/) } }) @@ -222,9 +282,15 @@ describe('same-cap roll scripts accept every same-cap cell', () => { }) it('validates a correct plan for every wave cell at that cell\'s rehome protocol', () => { - for (const [cellId, protocol] of SAME_CAP_CELLS.flatMap((cell) => [[cell, 1], [cell, 3]])) { + const trusted = SAME_CAP_CELLS.filter((cell) => REHOME_SOURCE_CELLS.has(cell)) + // Only a declared rehome source may roll at a trusted protocol at all; the job refuses + // the rest before it plans, and the next test covers them at protocol 0. + assert.deepEqual( + SAME_CAP_CELLS.filter((cell) => !REHOME_SOURCE_CELLS.has(cell)), + SAME_CAP_MIGRATION_ONLY_CELLS + ) + for (const [cellId, protocol] of trusted.flatMap((cell) => [[cell, 1], [cell, 3]])) { const { cap, pool } = cellShape(cellId) - assert.equal(REHOME_SOURCE_CELLS.has(cellId), true, cellId) const config = { mode: 'same-cap-cell', cellId, @@ -270,7 +336,7 @@ describe('same-cap roll scripts accept every same-cap cell', () => { const config = { mode: 'same-cap-cell', cellId, - hardCap: 1000, + hardCap: 600, unobservedBound: 60, image: TARGET_IMAGE, rollbackImage: ROLLBACK_IMAGE, @@ -278,7 +344,7 @@ describe('same-cap roll scripts accept every same-cap cell', () => { rehomeAudience: AUDIENCE, regionalRehomeProtocol: '0' } - const plan = rollPlan({ cellId, cap: 1000, protocol: 0 }) + const plan = rollPlan({ cellId, cap: 600, protocol: 0 }) assert.deepEqual(validateCapacityPlan(plan, config), { mode: 'same-cap-cell', changes: 2 }) // Protocol 1 must reject a plan with no rehome lines, or the absent-line rule decides nothing. assert.throws( @@ -287,6 +353,96 @@ describe('same-cap roll scripts accept every same-cap cell', () => { ) }) + it('resolves the class and selector delta the wave validator declares', () => { + for (const cellId of SAME_CAP_CELLS) { + const resolved = resolveCellClass(cellId) + assert.equal(resolved.status, 0, `${cellId}: ${resolved.stderr}`) + assert.equal( + resolved.stdout.trim(), + `${entryAdmission(cellId)} ${selectorWaveDelta(cellId)}`, + cellId + ) + } + assert.equal(resolveCellClass('production-gce-c12').status, 1) + }) + + it('offsets a later wave by this cell class\'s own selector delta', () => { + for (const [waveIndex, delta] of [['0', 2], ['3', 2], ['0', 0], ['3', 0]]) { + const resolved = spawnSync('bash', ['-euo', 'pipefail', '-c', generationBlock()], { + env: { + ...process.env, + DEPLOY_MODE: 'apply', + EXPECTED_SELECTOR_GENERATION: '40', + WAVE_INDEX: waveIndex, + SELECTOR_WAVE_DELTA: String(delta) + }, + encoding: 'utf8' + }) + assert.equal(resolved.status, 0, resolved.stderr) + assert.equal(resolved.stdout.trim(), String(40 + delta * Number(waveIndex))) + } + }) + + it('hands a migration-only cell back the exact membership it entered with', () => { + const entry = { + EXPECTED_MIGRATION_ONLY_CELLS: 'production-gce-c17,production-gce-c18', + EXPECTED_GENERAL_CELLS: 'production-gce-c7,production-gce-c8' + } + const isolated = membership({ + ...entry, + TARGET_CELL_ID: 'production-gce-c17', + ENTRY_ADMISSION: 'migration-only' + }) + assert.deepEqual(isolated, { + isolatedMigration: 'production-gce-c17,production-gce-c18', + isolatedGeneral: 'production-gce-c7,production-gce-c8', + restoredMigration: 'production-gce-c17,production-gce-c18', + restoredGeneral: 'production-gce-c7,production-gce-c8' + }) + // A general cell still leaves migration-only and returns to general. + assert.deepEqual( + membership({ + ...entry, + TARGET_CELL_ID: 'production-gce-c7', + ENTRY_ADMISSION: 'general' + }), + { + isolatedMigration: 'production-gce-c17,production-gce-c18,production-gce-c7', + isolatedGeneral: 'production-gce-c8', + restoredMigration: 'production-gce-c17,production-gce-c18', + restoredGeneral: 'production-gce-c7,production-gce-c8' + } + ) + }) + + it('never activates a migration-only cell and proves its isolate changed nothing', () => { + const restore = workflow + .split('name: Restore only the verified selected cell to its entry admission')[1] + .split('\n - id:')[0] + assert.match(restore, /if test "\$\{ENTRY_ADMISSION\}" = migration-only; then\n\s+RESTORE_MODE=isolate/) + assert.match(restore, /--admission "\$\{ENTRY_ADMISSION\}"/) + // The pre-mutation check must demand the class the cell is declared to serve in. + assert.match(workflow, /PRECHECK_ADMISSION="\$\{ENTRY_ADMISSION\}"/) + const isolate = workflow + .split('name: Reversibly isolate and drain only the selected cell')[1] + .split('\n - id:')[0] + assert.match(isolate, /migration-only; then\n\s+jq -e '\.changed == false'/) + }) + + it('requires rehome source membership exactly when a roll carries trust lines', () => { + const step = workflow + .split('name: Resolve immutable same-cap cell configuration')[1] + .split('\n - name:')[0] + const guard = step.indexOf('jq -e --arg cell "${TARGET_CELL_ID}" \'index($cell) != null\'') + assert.notEqual(guard, -1) + // The guard reads both protocols, so it has to sit after they are resolved. + assert.ok(step.indexOf('DESIRED_REHOME_PROTOCOL="${TARGET_REHOME_PROTOCOL}"') < guard) + assert.match( + step.slice(0, guard), + /test "\$\{DESIRED_REHOME_PROTOCOL\}" != 0 \|\| test "\$\{CURRENT_REHOME_PROTOCOL\}" != 0\n\s+\}; then\s+$/ + ) + }) + it('leaves the US-only capacity job on the default allowlist', () => { assert.doesNotMatch(capacityWorkflow, /--approved-cells/) }) diff --git a/cloud/docs/relay-workflows.md b/cloud/docs/relay-workflows.md index 0915d703c8b..d6b4dfde955 100644 --- a/cloud/docs/relay-workflows.md +++ b/cloud/docs/relay-workflows.md @@ -374,7 +374,8 @@ remain general at 1,000/60. The workflow lock, single-use evidence marker, exact targeted Terraform plan, and per-cell heartbeat/admission oracle are unchanged. `Deploy Relay Production Same-Cap` rolls only the reviewed US 1,000/60 and Asia 3,000/60 serving -sets without changing a cell's connection shape. Use `canary-apply` for exactly one cell. A successful canary +sets and the two migration-only US 600/60 cells, C17 and C18, without changing a cell's connection +shape. Use `canary-apply` for exactly one cell. A successful canary seals its commit, target and rollback digests, selector generation, and durable rehome generation; `batch-apply` accepts only that same authority and rolls two to four cells sequentially. Each cell is isolated, drained to two restart-safe samples, replaced from a targeted saved plan, and restored only @@ -384,6 +385,14 @@ director; the workflow never receives or mints a director or stamped-cell runtim keeps only the selected cell migration-only, while the exact rollback digest remains dispatchable via the same workflow's `rollback` mode. +C17 and C18 hold no hosts and are not general, so rolling one displaces nobody: they are the +zero-displacement canary for a new image. Their wave enters and leaves migration-only, so its +isolate and its restore are both no-ops and the selector generation does not move; a general +cell's wave still advances it by two. One wave may not mix the two classes, because every cell +after the first offsets from a single per-wave delta. Neither cell is a declared regional-rehome +source, so its template carries no rehome trust lines and it may roll only at rehome protocol `0`; +the job refuses a trusted protocol for it before it plans anything. + ### Gate override (break-glass) Every mutating same-cap wave normally consumes a fresh 15-minute aggregate monitor dry-run. From ff8f7085ccffcc35696e19784c28c1fb6db813d4 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 20:33:57 -0400 Subject: [PATCH 034/168] fix(relay-ops): bind the canary cell's admission class into same-cap batch authority (#21313) A batch-apply wave verified only that the sealed canary named some approved same-cap cell, so a canary rolled on the migration-only, zero-host, 600-cap c17 or c18 was accepted as authority for a general 1000/3000-cap batch. The verify step now hands the batch's own cells to the check, which requires the sealed cell's entry admission to equal the batch's class. --- ...cloud-deploy-relay-production-same-cap.yml | 14 +- .../relay-production-same-cap-wave.mjs | 11 ++ .../relay-production-same-cap-wave.test.mjs | 135 +++++++++++++++++- 3 files changed, 152 insertions(+), 8 deletions(-) diff --git a/.github/workflows/cloud-deploy-relay-production-same-cap.yml b/.github/workflows/cloud-deploy-relay-production-same-cap.yml index fa2cae818b2..421fa080072 100644 --- a/.github/workflows/cloud-deploy-relay-production-same-cap.yml +++ b/.github/workflows/cloud-deploy-relay-production-same-cap.yml @@ -181,14 +181,20 @@ jobs: if: ${{ inputs.mode == 'batch-apply' }} env: CANARY_RUN_ID: ${{ inputs.canary-run-id }} + CELL_IDS: ${{ inputs.cell-ids }} + TARGET_DIGEST: ${{ inputs.target-image-digest }} + ROLLBACK_DIGEST: ${{ inputs.rollback-image-digest }} + SELECTOR_GENERATION: ${{ inputs.expected-selector-generation }} + REHOME_GENERATION: ${{ inputs.expected-rehome-generation }} run: | node dev/scripts/relay-production-same-cap-wave.mjs verify-canary \ --file "${RUNNER_TEMP}/relay-same-cap-canary/authority.json" \ --commit-sha "${GITHUB_SHA}" --run-id "${CANARY_RUN_ID}" \ - --target-digest "${{ inputs.target-image-digest }}" \ - --rollback-digest "${{ inputs.rollback-image-digest }}" \ - --selector-generation "${{ inputs.expected-selector-generation }}" \ - --rehome-generation "${{ inputs.expected-rehome-generation }}" + --cell-ids "${CELL_IDS}" \ + --target-digest "${TARGET_DIGEST}" \ + --rollback-digest "${ROLLBACK_DIGEST}" \ + --selector-generation "${SELECTOR_GENERATION}" \ + --rehome-generation "${REHOME_GENERATION}" - name: Reject previously consumed aggregate safety evidence if: ${{ inputs.mode != 'verify' && inputs.gate-override-confirmation == '' }} diff --git a/cloud/dev/scripts/relay-production-same-cap-wave.mjs b/cloud/dev/scripts/relay-production-same-cap-wave.mjs index a3b6fee31b6..654d9fc55bd 100644 --- a/cloud/dev/scripts/relay-production-same-cap-wave.mjs +++ b/cloud/dev/scripts/relay-production-same-cap-wave.mjs @@ -134,6 +134,8 @@ export function canaryAuthority(input) { export function verifyCanaryAuthority(authority, expected, repositoryRoot) { const selectorGeneration = Number(expected.selectorGeneration) + // A mixed wave is already rejected, so the batch's first cell names the whole batch's class. + const batchAdmission = entryAdmission(cells(expected.cellIds ?? '')[0]) if ( authority?.v !== 1 || !/^[0-9a-f]{40}$/.test(authority.commitSha ?? '') || @@ -147,6 +149,14 @@ export function verifyCanaryAuthority(authority, expected, repositoryRoot) { authority.rehomeGeneration !== Number(expected.rehomeGeneration) || !SAME_CAP_CELLS.includes(authority.cellId) ) throw new Error('canary authority does not match this batch') + // A migration-only cell carries no hosts and a different cap, so rolling it proves nothing + // about a general batch, and its wave advances a different selector delta. + if (entryAdmission(authority.cellId) !== batchAdmission) { + throw new Error( + `canary authority cell ${authority.cellId} is ${entryAdmission(authority.cellId)}, ` + + `but this batch is ${batchAdmission}` + ) + } // Each cell checks exact live selector state; later batches may reuse this control epoch's canary. requireSameEvidenceCode({ sealedSha: authority.commitSha, @@ -215,6 +225,7 @@ export function main(argv = process.argv.slice(2)) { verifyCanaryAuthority(JSON.parse(readFileSync(input.file, 'utf8')), { commitSha: input['commit-sha'], runId: input['run-id'], + cellIds: input['cell-ids'], targetDigest: input['target-digest'], rollbackDigest: input['rollback-digest'], selectorGeneration: input['selector-generation'], diff --git a/cloud/dev/scripts/relay-production-same-cap-wave.test.mjs b/cloud/dev/scripts/relay-production-same-cap-wave.test.mjs index 997b5975968..0d2ac6f1a96 100644 --- a/cloud/dev/scripts/relay-production-same-cap-wave.test.mjs +++ b/cloud/dev/scripts/relay-production-same-cap-wave.test.mjs @@ -1,5 +1,5 @@ import assert from 'node:assert/strict' -import { execFileSync } from 'node:child_process' +import { execFileSync, spawnSync } from 'node:child_process' import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' @@ -13,6 +13,7 @@ import { validateSameCapWave, verifyCanaryAuthority } from './relay-production-same-cap-wave.mjs' +import { readRelayWorkflow } from './relay-repository.mjs' const targetDigest = `sha256:${'a'.repeat(64)}` const rollbackDigest = `sha256:${'b'.repeat(64)}` @@ -103,10 +104,11 @@ test('seals a migration-only canary at the generation its wave leaves behind', ( // Isolate and restore are both no-ops on a migration-only cell, so nothing advances. assert.equal(seal('production-gce-c17').selectorGeneration, 11) assert.equal(seal('production-gce-c7').selectorGeneration, 13) - // That canary still authorizes a later general batch; it is evidence about the image. + // That canary still authorizes a later batch of its own class; it is evidence about the image. assert.equal(verifyCanaryAuthority(seal('production-gce-c17'), { commitSha: 'c'.repeat(40), runId: '42', + cellIds: 'production-gce-c17,production-gce-c18', targetDigest, rollbackDigest, selectorGeneration: '11', @@ -173,6 +175,7 @@ test('seals and verifies canary authority for later batches', () => { assert.equal(verifyCanaryAuthority(authority, { commitSha: 'c'.repeat(40), runId: '42', + cellIds: 'production-gce-c8,production-gce-c9', targetDigest, rollbackDigest, selectorGeneration: '13', @@ -181,6 +184,7 @@ test('seals and verifies canary authority for later batches', () => { assert.throws(() => verifyCanaryAuthority(authority, { commitSha: 'd'.repeat(40), runId: '42', + cellIds: 'production-gce-c8,production-gce-c9', targetDigest, rollbackDigest, selectorGeneration: '11', @@ -195,8 +199,8 @@ test('reuses a canary across selector advances only within the same control epoc commitSha: 'c'.repeat(40), runId: '42', selectorGeneration: '11', rehomeGeneration: '4' }) const expected = { - commitSha: 'c'.repeat(40), runId: '42', targetDigest, rollbackDigest, - selectorGeneration: '21', rehomeGeneration: '4' + commitSha: 'c'.repeat(40), runId: '42', cellIds: 'production-gce-c8,production-gce-c9', + targetDigest, rollbackDigest, selectorGeneration: '21', rehomeGeneration: '4' } for (const generation of ['13', '14', '21', '29']) { assert.equal(verifyCanaryAuthority(authority, { @@ -270,6 +274,7 @@ test('a batch trusts a canary sealed by identical code at an ancestor commit', a const verifyAt = (commitSha, repositoryRoot) => verifyCanaryAuthority(authority, { commitSha, runId: '42', + cellIds: 'production-gce-c8,production-gce-c9', targetDigest, rollbackDigest, selectorGeneration: '21', @@ -392,6 +397,7 @@ test('seals the override into the canary authority as audit trail only', () => { const expected = { commitSha: 'f'.repeat(40), runId: '42', + cellIds: 'production-gce-c8,production-gce-c9', targetDigest, rollbackDigest, selectorGeneration: '21', @@ -416,3 +422,124 @@ test('seals the override into the canary authority as audit trail only', () => { 'production-gce-c7' ) }) + +function sealedCanary(cellId) { + return canaryAuthority({ + cellIds: cellId, + targetDigest, + rollbackDigest, + confirmation: `ROLL_RELAY_SAME_CAP ${targetDigest} ${cellId}`, + commitSha: 'c'.repeat(40), + runId: '42', + selectorGeneration: '11', + rehomeGeneration: '4' + }) +} + +// Why: a migration-only cell holds zero hosts at a different cap and its wave advances no +// selector, so rolling one is no evidence for a general batch, and the reverse is no evidence +// either. Nothing but the sealed cell id says which class a canary actually proved. +test('refuses a canary sealed on a cell of the other admission class', () => { + const expected = { + commitSha: 'c'.repeat(40), + runId: '42', + targetDigest, + rollbackDigest, + selectorGeneration: '99', + rehomeGeneration: '4' + } + const general = 'production-gce-c8,production-gce-c9' + const migrationOnly = SAME_CAP_MIGRATION_ONLY_CELLS.join(',') + assert.throws( + () => verifyCanaryAuthority(sealedCanary('production-gce-c17'), { + ...expected, cellIds: general + }), + /canary authority cell production-gce-c17 is migration-only, but this batch is general/ + ) + assert.throws( + () => verifyCanaryAuthority(sealedCanary('production-gce-c7'), { + ...expected, cellIds: migrationOnly + }), + /canary authority cell production-gce-c7 is general, but this batch is migration-only/ + ) + assert.equal( + verifyCanaryAuthority(sealedCanary('production-gce-c7'), { + ...expected, cellIds: general + }).cellId, + 'production-gce-c7' + ) + assert.equal( + verifyCanaryAuthority(sealedCanary('production-gce-c17'), { + ...expected, cellIds: migrationOnly + }).cellId, + 'production-gce-c17' + ) + // A caller that names no batch at all gets no verdict, rather than an unchecked class. + assert.throws( + () => verifyCanaryAuthority(sealedCanary('production-gce-c7'), expected), + /same-cap wave cells are invalid/ + ) +}) + +// The dispatch workflow is the only caller, so the class check only binds anything if that +// step actually hands the batch over; run the step's own shell exactly as written. +function verifyCanaryStepScript() { + const dispatch = readRelayWorkflow('deploy-relay-production-same-cap.yml') + const first = ' node dev/scripts/relay-production-same-cap-wave.mjs verify-canary \\\n' + const start = dispatch.indexOf(first) + assert.notEqual(start, -1, 'the dispatch workflow has no verify-canary step') + const last = ' --rehome-generation "${REHOME_GENERATION}"\n' + const end = dispatch.indexOf(last, start) + assert.notEqual(end, -1, 'the verify-canary step does not end at the rehome generation') + return dispatch.slice(start, end + last.length).replace(/^ {10}/gm, '') +} + +async function runVerifyCanaryStep(authority, cellIds) { + const temporary = await mkdtemp(join(tmpdir(), 'relay-same-cap-verify-')) + try { + await mkdir(join(temporary, 'relay-same-cap-canary'), { recursive: true }) + await writeFile( + join(temporary, 'relay-same-cap-canary', 'authority.json'), + JSON.stringify(authority) + ) + return spawnSync('bash', ['-euo', 'pipefail', '-c', verifyCanaryStepScript()], { + cwd: new URL('../..', import.meta.url), + env: { + ...process.env, + RUNNER_TEMP: temporary, + GITHUB_SHA: authority.commitSha, + CANARY_RUN_ID: authority.runId, + CELL_IDS: cellIds, + TARGET_DIGEST: targetDigest, + ROLLBACK_DIGEST: rollbackDigest, + SELECTOR_GENERATION: '99', + REHOME_GENERATION: '4' + }, + encoding: 'utf8' + }) + } finally { + await rm(temporary, { recursive: true, force: true }) + } +} + +test('the batch gate hands its own cells to the canary check', async () => { + const accepted = await runVerifyCanaryStep( + sealedCanary('production-gce-c7'), + 'production-gce-c8,production-gce-c9' + ) + assert.equal(accepted.status, 0, accepted.stderr) + const crossed = await runVerifyCanaryStep( + sealedCanary('production-gce-c17'), + 'production-gce-c8,production-gce-c9' + ) + assert.equal(crossed.status, 1, crossed.stdout) + assert.match( + crossed.stderr, + /canary authority cell production-gce-c17 is migration-only, but this batch is general/ + ) + const migrationOnly = await runVerifyCanaryStep( + sealedCanary('production-gce-c17'), + SAME_CAP_MIGRATION_ONLY_CELLS.join(',') + ) + assert.equal(migrationOnly.status, 0, migrationOnly.stderr) +}) From 1cd2964501acd5c0bc736323f09205d272664f09 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 20:35:04 -0400 Subject: [PATCH 035/168] perf(mobile): build the two projected git enums once, not per parse (#21311) `readProjectedConflictOperation` and `readProjectedCompareStatus` constructed a `z.enum` on every call, so every `git.status` and `git.branchCompare` reply paid the constructor. Hoisted to module constants; the git-status payload schema reuses the same instance. Behaviour is unchanged: same arms, same fallbacks, identical reader output on all eleven recorded matrix cases. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- mobile/src/session/diff-review-reply-schema.ts | 5 ++++- mobile/src/source-control/git-status-reply-schema.ts | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/mobile/src/session/diff-review-reply-schema.ts b/mobile/src/session/diff-review-reply-schema.ts index b35da5da76b..b875df72b05 100644 --- a/mobile/src/session/diff-review-reply-schema.ts +++ b/mobile/src/session/diff-review-reply-schema.ts @@ -27,6 +27,9 @@ const GIT_BRANCH_COMPARE_STATUS = [ type GitBranchCompareStatus = (typeof GIT_BRANCH_COMPARE_STATUS)[number] +// Built once: readProjectedCompareStatus runs on every reply. +const gitBranchCompareStatusSchema = z.enum(GIT_BRANCH_COMPARE_STATUS) + /** * One committed change. * @@ -101,7 +104,7 @@ export const branchCompareProjectionSchema: z.ZodType Date: Thu, 17 Sep 2026 20:42:34 -0400 Subject: [PATCH 036/168] fix(relay-ops): pin the capacity identity so a stale same-cap template can roll (#21314) c17's canary-apply failed closed at plan validation. Its instance template is from 2026-08-07 and predates the ORCA_RELAY_CAPACITY_SERVICE_ACCOUNT line that every cell rolled since already carries, so the plan legitimately added it. The same-cap validator holds the whole startup script identical before and after except the image, and that line is not one it excluded, so the wave stopped with nothing applied. Pin the line for same-cap-cell exactly as bootstrap-cell already does, and exclude it from the before/after comparison. The cell may gain it; the pin is what refuses a roll that drops it or rewrites it to another identity. Both plan validations in the job now pass the capacity identity the job already requires. The same-cap contract is otherwise unchanged: any other stale line still fails closed, and needs a convergence apply before the cell can roll. --- ...d-deploy-relay-production-same-cap-job.yml | 3 + .../relay-regional-rehome-workflow.test.mjs | 4 +- .../relay-same-cap-script-census.test.mjs | 81 +++++++++- .../scripts/validate-relay-capacity-plan.mjs | 20 ++- .../validate-relay-capacity-plan.test.mjs | 142 +++++++++++++++++- cloud/docs/relay-workflows.md | 7 + 6 files changed, 245 insertions(+), 12 deletions(-) diff --git a/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml b/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml index 050e47f4e8b..d643c9de6e9 100644 --- a/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml +++ b/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml @@ -541,6 +541,7 @@ jobs: if: ${{ inputs.mode != 'verify' && env.ROLLBACK_RESUME == 'true' }} shell: bash env: + CAPACITY_SERVICE_ACCOUNT: ${{ vars.PRODUCTION_GCP_RELAY_CAPACITY_SERVICE_ACCOUNT }} DIRECTOR_RUNTIME_SERVICE_ACCOUNT: ${{ vars.PRODUCTION_GCP_RELAY_DIRECTOR_RUNTIME_SERVICE_ACCOUNT }} run: | # A cell on the root pool default emits no pool line, so pin one only where it exists. @@ -586,6 +587,7 @@ jobs: --unobserved-bound "${EXPECTED_UNOBSERVED_BOUND}" \ --image "${DESIRED_IMAGE}" \ --rollback-image "${DESIRED_IMAGE}" \ + --capacity-service-account "${CAPACITY_SERVICE_ACCOUNT}" \ --rehome-director-service-account "${DIRECTOR_RUNTIME_SERVICE_ACCOUNT}" \ --rehome-audience https://relay.onorca.dev/v1/admin/host-drain \ --regional-rehome-protocol "${DESIRED_REHOME_PROTOCOL}" \ @@ -619,6 +621,7 @@ jobs: --hard-cap "${EXPECTED_HARD_CAP}" \ --unobserved-bound "${EXPECTED_UNOBSERVED_BOUND}" --image "${DESIRED_IMAGE}" \ --rollback-image "${IMAGE_REPOSITORY}@${CURRENT_IMAGE_DIGEST}" \ + --capacity-service-account "${CAPACITY_SERVICE_ACCOUNT}" \ --rehome-director-service-account "${DIRECTOR_RUNTIME_SERVICE_ACCOUNT}" \ --rehome-audience https://relay.onorca.dev/v1/admin/host-drain \ --regional-rehome-protocol "${DESIRED_REHOME_PROTOCOL}" \ diff --git a/cloud/dev/scripts/relay-regional-rehome-workflow.test.mjs b/cloud/dev/scripts/relay-regional-rehome-workflow.test.mjs index 829005df7fc..e5294124466 100644 --- a/cloud/dev/scripts/relay-regional-rehome-workflow.test.mjs +++ b/cloud/dev/scripts/relay-regional-rehome-workflow.test.mjs @@ -67,11 +67,11 @@ test('same-cap wrapper is reusable, canary-bound, and sequential', () => { ) assert.match( job, - /Require converged Terraform state and a stable MIG on resume[\s\S]{0,200}DIRECTOR_RUNTIME_SERVICE_ACCOUNT: \$\{\{ vars\.PRODUCTION_GCP_RELAY_DIRECTOR_RUNTIME_SERVICE_ACCOUNT \}\}/ + /Require converged Terraform state and a stable MIG on resume[\s\S]{0,300}CAPACITY_SERVICE_ACCOUNT: \$\{\{ vars\.PRODUCTION_GCP_RELAY_CAPACITY_SERVICE_ACCOUNT \}\}\n {10}DIRECTOR_RUNTIME_SERVICE_ACCOUNT: \$\{\{ vars\.PRODUCTION_GCP_RELAY_DIRECTOR_RUNTIME_SERVICE_ACCOUNT \}\}/ ) assert.match( job, - /--rollback-image "\$\{DESIRED_IMAGE\}" \\\n {16}--rehome-director-service-account "\$\{DIRECTOR_RUNTIME_SERVICE_ACCOUNT\}"/ + /--rollback-image "\$\{DESIRED_IMAGE\}" \\\n {16}--capacity-service-account "\$\{CAPACITY_SERVICE_ACCOUNT\}" \\\n {16}--rehome-director-service-account "\$\{DIRECTOR_RUNTIME_SERVICE_ACCOUNT\}"/ ) assert.match( job, diff --git a/cloud/dev/scripts/relay-same-cap-script-census.test.mjs b/cloud/dev/scripts/relay-same-cap-script-census.test.mjs index f6539db2f79..7cf4223c73c 100644 --- a/cloud/dev/scripts/relay-same-cap-script-census.test.mjs +++ b/cloud/dev/scripts/relay-same-cap-script-census.test.mjs @@ -20,6 +20,7 @@ const production = readFileSync( ) const REHOME_SOURCE_CELLS = rehomeSourceCells() const DIRECTOR_IDENTITY = 'relay-director@onorca-cloud.iam.gserviceaccount.com' +const CAPACITY_IDENTITY = 'orca-cloud-gha-cap@onorca-cloud.iam.gserviceaccount.com' const AUDIENCE = 'https://relay.onorca.dev/v1/admin/host-drain' const ROLLBACK_IMAGE = `us-central1-docker.pkg.dev/p/orca-cloud/relay@sha256:${'d'.repeat(64)}` const TARGET_IMAGE = `us-central1-docker.pkg.dev/p/orca-cloud/relay@sha256:${'e'.repeat(64)}` @@ -53,10 +54,13 @@ function tfvarsCellBlock(cellId) { return production.slice(start, production.indexOf('\n }', start)) } -function startupScript({ cap, image, trusted, pool }) { +function startupScript({ cap, image, trusted, pool, capacityIdentity = CAPACITY_IDENTITY }) { return [ ` printf 'ORCA_RELAY_CELL_CONNECTION_HARD_CAP=%s\\n' '${cap}'`, ` printf 'ORCA_RELAY_CELL_CONNECTION_UNOBSERVED_BOUND=%s\\n' '60'`, + ...(capacityIdentity === null + ? [] + : [` printf 'ORCA_RELAY_CAPACITY_SERVICE_ACCOUNT=%s\\n' '${capacityIdentity}'`]), ...(pool === undefined ? [] : [` printf 'ORCA_RELAY_DATABASE_POOL_MAX=%s\\n' '${pool}'`]), @@ -73,7 +77,11 @@ function startupScript({ cap, image, trusted, pool }) { } // The exact shape the apply step's plan has: template replaced, MIG rebound to it. -function rollPlan({ cellId, cap, protocol, pool }) { +function rollPlan({ + cellId, cap, protocol, pool, + beforeCapacityIdentity = CAPACITY_IDENTITY, + afterCapacityIdentity = CAPACITY_IDENTITY +}) { return { configuration: { root_module: { @@ -104,7 +112,8 @@ function rollPlan({ cellId, cap, protocol, pool }) { image: ROLLBACK_IMAGE, trusted: protocol >= 1, // The live template predates the reviewed pool raise, as every asia cell's does. - pool: pool === undefined ? undefined : '10' + pool: pool === undefined ? undefined : '10', + capacityIdentity: beforeCapacityIdentity }) }, after: { @@ -112,7 +121,8 @@ function rollPlan({ cellId, cap, protocol, pool }) { cap, image: TARGET_IMAGE, trusted: protocol >= 1, - pool + pool, + capacityIdentity: afterCapacityIdentity }), self_link: null }, @@ -298,6 +308,7 @@ describe('same-cap roll scripts accept every same-cap cell', () => { unobservedBound: 60, image: TARGET_IMAGE, rollbackImage: ROLLBACK_IMAGE, + capacityServiceAccount: CAPACITY_IDENTITY, rehomeDirectorServiceAccount: DIRECTOR_IDENTITY, rehomeAudience: AUDIENCE, regionalRehomeProtocol: String(protocol), @@ -340,6 +351,7 @@ describe('same-cap roll scripts accept every same-cap cell', () => { unobservedBound: 60, image: TARGET_IMAGE, rollbackImage: ROLLBACK_IMAGE, + capacityServiceAccount: CAPACITY_IDENTITY, rehomeDirectorServiceAccount: DIRECTOR_IDENTITY, rehomeAudience: AUDIENCE, regionalRehomeProtocol: '0' @@ -443,6 +455,67 @@ describe('same-cap roll scripts accept every same-cap cell', () => { ) }) + it('rolls a template stale enough to predate the pinned capacity identity', () => { + // Exactly c17's shape on 2026-09-18: its live template is from 2026-08-07 and has no + // capacity identity line, so the roll adds one. Run 35290908836 failed closed here. + const cellId = 'production-gce-c17' + const config = { + mode: 'same-cap-cell', + cellId, + hardCap: 600, + unobservedBound: 60, + image: TARGET_IMAGE, + rollbackImage: ROLLBACK_IMAGE, + capacityServiceAccount: CAPACITY_IDENTITY, + rehomeDirectorServiceAccount: DIRECTOR_IDENTITY, + rehomeAudience: AUDIENCE, + regionalRehomeProtocol: '0' + } + const stale = rollPlan({ cellId, cap: 600, protocol: 0, beforeCapacityIdentity: null }) + assert.deepEqual(validateCapacityPlan(stale, config), { mode: 'same-cap-cell', changes: 2 }) + // The line may only be gained. A roll may not rewrite it, + assert.throws( + () => validateCapacityPlan(stale, { + ...config, + capacityServiceAccount: 'orca-cloud-gha-other@onorca-cloud.iam.gserviceaccount.com' + }), + /reviewed image and capacity/ + ) + // nor drop it from a template that already carries one. + assert.throws( + () => validateCapacityPlan( + rollPlan({ cellId, cap: 600, protocol: 0, afterCapacityIdentity: null }), + config + ), + /reviewed image and capacity/ + ) + // A same-cap roll cannot run without the identity pinned at all. + assert.throws( + () => validateCapacityPlan(stale, { ...config, capacityServiceAccount: undefined }), + /invalid service account/ + ) + }) + + it('pins the capacity identity on every plan validation the job runs', () => { + const invocations = workflow.split('validate-relay-capacity-plan.mjs').slice(1) + assert.equal(invocations.length, 2) + for (const invocation of invocations) { + const lines = invocation.split('\n') + const end = lines.findIndex((line) => !line.trimEnd().endsWith('\\')) + assert.match( + lines.slice(0, end + 1).join(' '), + /--capacity-service-account "\$\{CAPACITY_SERVICE_ACCOUNT\}"/ + ) + } + // Both steps must read it from the same repository variable the job already requires. + assert.equal( + workflow.split( + 'CAPACITY_SERVICE_ACCOUNT: ${{ vars.PRODUCTION_GCP_RELAY_CAPACITY_SERVICE_ACCOUNT }}' + ).length, + 4 + ) + }) + it('leaves the US-only capacity job on the default allowlist', () => { assert.doesNotMatch(capacityWorkflow, /--approved-cells/) }) diff --git a/cloud/dev/scripts/validate-relay-capacity-plan.mjs b/cloud/dev/scripts/validate-relay-capacity-plan.mjs index 3378d78704d..c81cfc8ca55 100644 --- a/cloud/dev/scripts/validate-relay-capacity-plan.mjs +++ b/cloud/dev/scripts/validate-relay-capacity-plan.mjs @@ -47,7 +47,10 @@ export function parseCapacityPlanArguments(argv) { return value } if (!values.image) throw new Error('missing --image') - if (values.mode === 'bootstrap-cell' && !values['capacity-service-account']) { + if ( + ['bootstrap-cell', 'same-cap-cell'].includes(values.mode) && + !values['capacity-service-account'] + ) { throw new Error('missing --capacity-service-account') } if ( @@ -239,7 +242,10 @@ function requireDesiredStartupScript(script, config) { ` printf 'ORCA_RELAY_CELL_CONNECTION_UNOBSERVED_BOUND=%s\\n' '${config.unobservedBound}'` ] ] - if (config.mode === 'bootstrap-cell') { + // A same-cap cell whose template predates this line gains it on its next roll, so the + // before/after comparison ignores it; pinning the exact identity here is what reviews it, + // and what stops a roll dropping or rewriting the line it lets through. + if (['bootstrap-cell', 'same-cap-cell'].includes(config.mode)) { expected.push([ /^ printf 'ORCA_RELAY_CAPACITY_SERVICE_ACCOUNT=%s\\n' '[a-z][a-z0-9-]{4,28}[a-z0-9]@[a-z0-9-]+\.iam\.gserviceaccount\.com'$/, ` printf 'ORCA_RELAY_CAPACITY_SERVICE_ACCOUNT=%s\\n' '${config.capacityServiceAccount}'` @@ -454,18 +460,22 @@ function cellPlan(plan, changes, config) { const sameCap = ['same-cap-cell', 'same-cap-image'].includes(config.mode) // Only a pinned pool may move here; requireDesiredStartupScript holds the after value exactly. const stripPool = config.mode === 'same-cap-cell' && config.databasePoolMax !== undefined + // Only a template stale enough to predate the line may move it, and only by gaining it; + // requireDesiredStartupScript holds the after value to the exact reviewed identity. + const stripCapacityIdentity = + ['bootstrap-cell', 'same-cap-cell'].includes(config.mode) if ( typeof beforeScript !== 'string' || (sameCap && relayImage(beforeScript) !== config.rollbackImage) || normalizedStartupScript( beforeScript, - config.mode === 'bootstrap-cell', + stripCapacityIdentity, config.mode === 'same-cap-cell', sameCap, stripPool ) !== normalizedStartupScript( script, - config.mode === 'bootstrap-cell', + stripCapacityIdentity, config.mode === 'same-cap-cell', sameCap, stripPool @@ -501,7 +511,7 @@ export function validateCapacityPlan(plan, config) { throw new Error('capacity Terraform plans may change only a cell') } if ( - config.mode === 'bootstrap-cell' && + ['bootstrap-cell', 'same-cap-cell'].includes(config.mode) && !SERVICE_ACCOUNT_EMAIL.test(config.capacityServiceAccount ?? '') ) { throw new Error('capacity Terraform plan has an invalid service account') diff --git a/cloud/dev/scripts/validate-relay-capacity-plan.test.mjs b/cloud/dev/scripts/validate-relay-capacity-plan.test.mjs index 2953e0c2f92..9094e9e0e7a 100644 --- a/cloud/dev/scripts/validate-relay-capacity-plan.test.mjs +++ b/cloud/dev/scripts/validate-relay-capacity-plan.test.mjs @@ -428,10 +428,14 @@ test('same-cap mode preserves 1000/60 while adding only the reviewed trust confi const rollbackImage = `us-docker.pkg.dev/project/relay/image@sha256:${'d'.repeat(64)}` const image = `us-docker.pkg.dev/project/relay/image@sha256:${'e'.repeat(64)}` const directorIdentity = 'relay-director@project.iam.gserviceaccount.com' + const capacityIdentity = 'orca-cloud-gha-cap@project.iam.gserviceaccount.com' const audience = 'https://relay.example.com/v1/admin/host-drain' - const startup = ({ selectedImage, cap = 1_000, trust = false }) => [ + const startup = ({ selectedImage, cap = 1_000, trust = false, capacity = capacityIdentity }) => [ ` printf 'ORCA_RELAY_CELL_CONNECTION_HARD_CAP=%s\\n' '${cap}'`, ` printf 'ORCA_RELAY_CELL_CONNECTION_UNOBSERVED_BOUND=%s\\n' '60'`, + ...(capacity === null + ? [] + : [` printf 'ORCA_RELAY_CAPACITY_SERVICE_ACCOUNT=%s\\n' '${capacity}'`]), ...(trust ? [ ` printf 'ORCA_RELAY_REHOME_DIRECTOR_SERVICE_ACCOUNT=%s\\n' '${directorIdentity}'`, ` printf 'ORCA_RELAY_REHOME_AUDIENCE=%s\\n' '${audience}'` @@ -468,6 +472,7 @@ test('same-cap mode preserves 1000/60 while adding only the reviewed trust confi mode: 'same-cap-cell', image, rollbackImage, + capacityServiceAccount: capacityIdentity, rehomeDirectorServiceAccount: directorIdentity, rehomeAudience: audience, regionalRehomeProtocol: '1' @@ -653,10 +658,12 @@ test('protocol-0 same-cap cells roll without rehome trust lines', () => { const rollbackImage = `us-docker.pkg.dev/project/relay/image@sha256:${'d'.repeat(64)}` const image = `us-docker.pkg.dev/project/relay/image@sha256:${'e'.repeat(64)}` const directorIdentity = 'relay-director@project.iam.gserviceaccount.com' + const capacityIdentity = 'orca-cloud-gha-cap@project.iam.gserviceaccount.com' const audience = 'https://relay.example.com/v1/admin/host-drain' const startup = ({ selectedImage, trust = false }) => [ ` printf 'ORCA_RELAY_CELL_CONNECTION_HARD_CAP=%s\\n' '3000'`, ` printf 'ORCA_RELAY_CELL_CONNECTION_UNOBSERVED_BOUND=%s\\n' '60'`, + ` printf 'ORCA_RELAY_CAPACITY_SERVICE_ACCOUNT=%s\\n' '${capacityIdentity}'`, ` printf 'ORCA_RELAY_CELL_REGION=%s\\n' 'asia-east2'`, ...(trust ? [ ` printf 'ORCA_RELAY_REHOME_DIRECTOR_SERVICE_ACCOUNT=%s\\n' '${directorIdentity}'`, @@ -693,6 +700,7 @@ test('protocol-0 same-cap cells roll without rehome trust lines', () => { mode: 'same-cap-cell', image, rollbackImage, + capacityServiceAccount: capacityIdentity, rehomeDirectorServiceAccount: directorIdentity, rehomeAudience: audience, regionalRehomeProtocol: '0' @@ -737,6 +745,133 @@ test('protocol-0 same-cap cells roll without rehome trust lines', () => { } }) +test('a same-cap roll may gain the pinned capacity identity but never move it', () => { + const rollbackImage = `us-docker.pkg.dev/project/relay/image@sha256:${'d'.repeat(64)}` + const image = `us-docker.pkg.dev/project/relay/image@sha256:${'e'.repeat(64)}` + const directorIdentity = 'relay-director@project.iam.gserviceaccount.com' + const capacityIdentity = 'orca-cloud-gha-cap@project.iam.gserviceaccount.com' + const audience = 'https://relay.example.com/v1/admin/host-drain' + const startup = ({ selectedImage, capacity }) => [ + ` printf 'ORCA_RELAY_CELL_CONNECTION_HARD_CAP=%s\\n' '600'`, + ` printf 'ORCA_RELAY_CELL_CONNECTION_UNOBSERVED_BOUND=%s\\n' '60'`, + ...(capacity === null + ? [] + : [` printf 'ORCA_RELAY_CAPACITY_SERVICE_ACCOUNT=%s\\n' '${capacity}'`]), + `printf 'ORCA_RELAY_IMAGE_DIGEST=%s\\n' '${selectedImage.split('@')[1]}'`, + `docker pull '${selectedImage}'`, + 'docker run --detach \\', + ' --name orca-relay \\', + ` '${selectedImage}'` + ].join('\n') + const plan = (beforeCapacity, afterCapacity) => ({ + resource_changes: [ + { + address: 'google_compute_instance_template.relay_gce_cell["production-gce-c17"]', + change: { + actions: ['create', 'delete'], + before: { + metadata_startup_script: startup({ + selectedImage: rollbackImage, + capacity: beforeCapacity + }) + }, + after: { + metadata_startup_script: startup({ selectedImage: image, capacity: afterCapacity }), + self_link: null + }, + after_unknown: { self_link: true } + } + }, + { + address: 'google_compute_instance_group_manager.relay_gce_cell["production-gce-c17"]', + change: { + actions: ['update'], + before: { target_size: 1, version: [{ instance_template: 'old' }] }, + after: { target_size: 1, version: [{ instance_template: null }] }, + after_unknown: { version: [{ instance_template: true }] } + } + } + ] + }) + const config = { + cellId: 'production-gce-c17', + hardCap: 600, + unobservedBound: 60, + mode: 'same-cap-cell', + image, + rollbackImage, + capacityServiceAccount: capacityIdentity, + rehomeDirectorServiceAccount: directorIdentity, + rehomeAudience: audience, + regionalRehomeProtocol: '0' + } + // A template old enough to predate the line gains it, which is the only move allowed. + assert.deepEqual( + validateCapacityPlan(plan(null, capacityIdentity), config), + { mode: 'same-cap-cell', changes: 2 } + ) + assert.deepEqual( + validateCapacityPlan(plan(capacityIdentity, capacityIdentity), config), + { mode: 'same-cap-cell', changes: 2 } + ) + for (const [before, after] of [ + [capacityIdentity, null], + [null, null], + [capacityIdentity, 'orca-cloud-gha-other@project.iam.gserviceaccount.com'], + [null, 'orca-cloud-gha-other@project.iam.gserviceaccount.com'] + ]) { + assert.throws( + () => validateCapacityPlan(plan(before, after), config), + /reviewed image and capacity/, + `${before} -> ${after}` + ) + } + // Without the pin there is nothing reviewing the line the comparison now ignores. + assert.throws( + () => validateCapacityPlan(plan(null, capacityIdentity), { + ...config, + capacityServiceAccount: undefined + }), + /invalid service account/ + ) + assert.throws( + () => validateCapacityPlan(plan(null, capacityIdentity), { + ...config, + capacityServiceAccount: 'not-an-email' + }), + /invalid service account/ + ) +}) + +test('the capacity identity argument is required by same-cap-cell mode', () => { + const image = `us-docker.pkg.dev/project/relay/image@sha256:${'e'.repeat(64)}` + const rollbackImage = `us-docker.pkg.dev/project/relay/image@sha256:${'d'.repeat(64)}` + const base = [ + '--mode', 'same-cap-cell', + '--cell-id', 'production-gce-c17', + '--hard-cap', '600', + '--unobserved-bound', '60', + '--image', image, + '--rollback-image', rollbackImage, + '--rehome-director-service-account', 'relay-director@project.iam.gserviceaccount.com', + '--rehome-audience', 'https://relay.onorca.dev/v1/admin/host-drain', + '--regional-rehome-protocol', '0' + ] + assert.throws(() => parseCapacityPlanArguments(base), /missing --capacity-service-account/) + assert.throws( + () => parseCapacityPlanArguments([...base, '--capacity-service-account', 'nope']), + /--capacity-service-account is invalid/ + ) + assert.equal( + parseCapacityPlanArguments([ + ...base, + '--capacity-service-account', + 'orca-cloud-gha-cap@project.iam.gserviceaccount.com' + ]).capacityServiceAccount, + 'orca-cloud-gha-cap@project.iam.gserviceaccount.com' + ) +}) + test('the rehome protocol argument is required by same-cap-cell mode alone', () => { const image = `us-docker.pkg.dev/project/relay/image@sha256:${'e'.repeat(64)}` const rollbackImage = `us-docker.pkg.dev/project/relay/image@sha256:${'d'.repeat(64)}` @@ -747,6 +882,7 @@ test('the rehome protocol argument is required by same-cap-cell mode alone', () '--unobserved-bound', '60', '--image', image, '--rollback-image', rollbackImage, + '--capacity-service-account', 'orca-cloud-gha-cap@project.iam.gserviceaccount.com', '--rehome-director-service-account', 'relay-director@project.iam.gserviceaccount.com', '--rehome-audience', 'https://relay.onorca.dev/v1/admin/host-drain', ...extra @@ -788,10 +924,12 @@ test('the reviewed database pool is pinned for the cells that emit one', () => { const rollbackImage = `us-docker.pkg.dev/project/relay/image@sha256:${'d'.repeat(64)}` const image = `us-docker.pkg.dev/project/relay/image@sha256:${'e'.repeat(64)}` const directorIdentity = 'relay-director@project.iam.gserviceaccount.com' + const capacityIdentity = 'orca-cloud-gha-cap@project.iam.gserviceaccount.com' const audience = 'https://relay.example.com/v1/admin/host-drain' const startup = ({ selectedImage, pool }) => [ ` printf 'ORCA_RELAY_CELL_CONNECTION_HARD_CAP=%s\\n' '3000'`, ` printf 'ORCA_RELAY_CELL_CONNECTION_UNOBSERVED_BOUND=%s\\n' '60'`, + ` printf 'ORCA_RELAY_CAPACITY_SERVICE_ACCOUNT=%s\\n' '${capacityIdentity}'`, ...(pool === undefined ? [] : [` printf 'ORCA_RELAY_DATABASE_POOL_MAX=%s\\n' '${pool}'`]), @@ -837,6 +975,7 @@ test('the reviewed database pool is pinned for the cells that emit one', () => { mode: 'same-cap-cell', image, rollbackImage, + capacityServiceAccount: capacityIdentity, rehomeDirectorServiceAccount: directorIdentity, rehomeAudience: audience, regionalRehomeProtocol: '1' @@ -891,6 +1030,7 @@ test('the database pool argument is accepted by same-cap-cell mode alone', () => '--unobserved-bound', '60', '--image', image, '--rollback-image', rollbackImage, + '--capacity-service-account', 'orca-cloud-gha-cap@project.iam.gserviceaccount.com', '--rehome-director-service-account', 'relay-director@project.iam.gserviceaccount.com', '--rehome-audience', 'https://relay.onorca.dev/v1/admin/host-drain', '--regional-rehome-protocol', '1', diff --git a/cloud/docs/relay-workflows.md b/cloud/docs/relay-workflows.md index d6b4dfde955..4c358c7e0ae 100644 --- a/cloud/docs/relay-workflows.md +++ b/cloud/docs/relay-workflows.md @@ -385,6 +385,13 @@ director; the workflow never receives or mints a director or stamped-cell runtim keeps only the selected cell migration-only, while the exact rollback digest remains dispatchable via the same workflow's `rollback` mode. +A roll holds the whole startup script identical before and after except the image, so a +template stale enough to predate a pinned line fails closed rather than absorbing the drift. +The one exception is the capacity identity: a cell that predates it gains it on its next roll, +and the plan validator pins the exact reviewed identity instead of comparing that line, so a +roll can never drop or rewrite it. Any other stale line still fails closed and needs a +convergence apply first. + C17 and C18 hold no hosts and are not general, so rolling one displaces nobody: they are the zero-displacement canary for a new image. Their wave enters and leaves migration-only, so its isolate and its restore are both no-ops and the selector generation does not move; a general From 27bddc619818f76d98c55e23f49ac2f69bef5144 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 20:58:07 -0400 Subject: [PATCH 037/168] fix(relay-ops): accept a drained predecessor on a cell that holds no hosts (#21315) c17's canary stopped at the pre-apply predecessor check with `runtime predecessor mismatch fields=draining`. The flag is residue: the previous canary (run 35290908836) drained c17 at 00:26:01, its terraform apply then failed, and the failsafe re-isolates without restarting the VM, so nothing cleared it. The same run had passed this very check a second earlier, which is what proves a parked cell is not draining at rest. Draining means connections are being shed, and a migration-only cell holds none, so the flag is not a precondition there. Accept it on entry for that class only. The replacement VM is still required not to be draining, on every path, and the incarnation check still proves it was replaced. Both predecessor checks now read one decision instead of computing the rule twice, so the assertion and its diagnostic cannot disagree. Every general-cell and rollback path keeps the value it had; a census test runs the real block over all eight mode and class combinations to hold that. --- ...d-deploy-relay-production-same-cap-job.yml | 42 ++++++++----- .../relay-same-cap-script-census.test.mjs | 59 +++++++++++++++++++ cloud/docs/relay-workflows.md | 6 ++ 3 files changed, 92 insertions(+), 15 deletions(-) diff --git a/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml b/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml index d643c9de6e9..2e1bf65d892 100644 --- a/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml +++ b/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml @@ -374,6 +374,31 @@ jobs: PREDECESSOR_IMAGE_DIGEST="${CURRENT_IMAGE_DIGEST}" PREDECESSOR_REHOME_PROTOCOL="${CURRENT_REHOME_PROTOCOL}" fi + # Rollback is the documented recovery from a failed canary, which + # leaves the cell migration-only (and possibly still marked + # draining); apply and verify still require the cell pristine in the + # class it is declared to serve in. + if test "${DEPLOY_MODE}" = rollback; then + PRECHECK_ADMISSION=general-or-migration-only + else + PRECHECK_ADMISSION="${ENTRY_ADMISSION}" + fi + # Draining sheds connections, and a migration-only cell holds none, so the flag + # carries no precondition there. It also outlives a failed wave, because the drain + # that set it is followed by no restart, which is the state a failed canary leaves. + if test "${DEPLOY_MODE}" = rollback \ + || test "${ENTRY_ADMISSION}" = migration-only; then + PRECHECK_DRAINING=either + else + PRECHECK_DRAINING=forbidden + fi + # A resumed rollback already restarted, so its cell has to come back not draining; + # that is what separates it from a wave that stopped before its template apply. + if test "${PRECHECK_DRAINING}" = either && test "${ROLLBACK_RESUME}" != true; then + PREDECESSOR_DRAINING_OK=true + else + PREDECESSOR_DRAINING_OK=false + fi RESTORED_MIGRATION_CELLS="$(jq -rn \ --arg value "${EXPECTED_MIGRATION_ONLY_CELLS/none/}" \ --arg target "${TARGET_CELL_ID}" \ @@ -420,8 +445,7 @@ jobs: --argjson hardCap "${EXPECTED_HARD_CAP}" \ --argjson unobservedBound "${EXPECTED_UNOBSERVED_BOUND}" \ --argjson protocol "${PREDECESSOR_REHOME_PROTOCOL}" \ - --argjson drainingOk "$(test "${DEPLOY_MODE}" = rollback \ - && test "${ROLLBACK_RESUME}" != true && echo true || echo false)" \ + --argjson drainingOk "${PREDECESSOR_DRAINING_OK}" \ '.role == "cell" and .cellId == $cell and .cellUrl == $origin and (.region == $region or ($region == "us-central1" and $protocol == 0 and .region == null)) and @@ -437,8 +461,7 @@ jobs: --argjson hardCap "${EXPECTED_HARD_CAP}" \ --argjson unobservedBound "${EXPECTED_UNOBSERVED_BOUND}" \ --argjson protocol "${PREDECESSOR_REHOME_PROTOCOL}" \ - --argjson drainingOk "$(test "${DEPLOY_MODE}" = rollback \ - && test "${ROLLBACK_RESUME}" != true && echo true || echo false)" \ + --argjson drainingOk "${PREDECESSOR_DRAINING_OK}" \ '[ if .role != "cell" then "role" else empty end, if .cellId != $cell then "cellId" else empty end, @@ -474,17 +497,6 @@ jobs: fi [[ "${SOURCE_INCARNATION}" =~ ^[0-9a-f-]{36}$ ]] echo "SOURCE_INCARNATION=${SOURCE_INCARNATION}" >> "${GITHUB_ENV}" - # Rollback is the documented recovery from a failed canary, which - # leaves the cell migration-only (and possibly still marked - # draining); apply and verify still require the cell pristine in the - # class it is declared to serve in. - if test "${DEPLOY_MODE}" = rollback; then - PRECHECK_ADMISSION=general-or-migration-only - PRECHECK_DRAINING=either - else - PRECHECK_ADMISSION="${ENTRY_ADMISSION}" - PRECHECK_DRAINING=forbidden - fi node dev/scripts/verify-relay-capacity-transition.mjs \ --director-origin "${DIRECTOR_ORIGIN}" --cell-origin "${CELL_ORIGIN}" \ --cell-id "${TARGET_CELL_ID}" --hard-cap "${EXPECTED_HARD_CAP}" \ diff --git a/cloud/dev/scripts/relay-same-cap-script-census.test.mjs b/cloud/dev/scripts/relay-same-cap-script-census.test.mjs index 7cf4223c73c..399a5cab6cb 100644 --- a/cloud/dev/scripts/relay-same-cap-script-census.test.mjs +++ b/cloud/dev/scripts/relay-same-cap-script-census.test.mjs @@ -182,6 +182,13 @@ function resolveCellClass(cellId) { ], { cwd: new URL('../..', import.meta.url), env: { ...process.env, TARGET_CELL_ID: cellId }, encoding: 'utf8' }) } +function drainingBlock() { + return `${jobBlock( + ' # Rollback is the documented recovery from a failed canary, which', + ' PREDECESSOR_DRAINING_OK=false\n fi' + )}\necho "\${PRECHECK_ADMISSION} \${PRECHECK_DRAINING} \${PREDECESSOR_DRAINING_OK}"` +} + function generationBlock() { return `${jobBlock( ' if test "${DEPLOY_MODE}" = verify; then', @@ -516,6 +523,58 @@ describe('same-cap roll scripts accept every same-cap cell', () => { ) }) + it('decides the predecessor draining rule from the real block, for both classes', () => { + // A zero-host cell sheds nothing, and a failed canary's own drain leaves the flag set + // with no restart behind it; run 35292335415 stopped on exactly that residue. + const cases = [ + // mode, entry class, resume, expected [precheck admission, precheck draining, jq ok] + ['apply', 'migration-only', 'false', ['migration-only', 'either', 'true']], + ['apply', 'general', 'false', ['general', 'forbidden', 'false']], + ['verify', 'migration-only', 'false', ['migration-only', 'either', 'true']], + ['verify', 'general', 'false', ['general', 'forbidden', 'false']], + // Every rollback path keeps exactly the behaviour it had. + ['rollback', 'general', 'false', ['general-or-migration-only', 'either', 'true']], + ['rollback', 'general', 'true', ['general-or-migration-only', 'either', 'false']], + ['rollback', 'migration-only', 'false', ['general-or-migration-only', 'either', 'true']], + ['rollback', 'migration-only', 'true', ['general-or-migration-only', 'either', 'false']] + ] + for (const [mode, entry, resume, expected] of cases) { + const resolved = spawnSync('bash', ['-euo', 'pipefail', '-c', drainingBlock()], { + env: { + ...process.env, + DEPLOY_MODE: mode, + ENTRY_ADMISSION: entry, + ROLLBACK_RESUME: resume + }, + encoding: 'utf8' + }) + assert.equal(resolved.status, 0, `${mode}/${entry}/${resume}: ${resolved.stderr}`) + assert.deepEqual( + resolved.stdout.trim().split(' '), + expected, + `${mode}/${entry}/${resume}` + ) + } + }) + + it('reads one draining decision in both predecessor checks', () => { + const step = workflow + .split('name: Verify exact current generation, digest, cap, and rollback point')[1] + .split('\n - name:')[0] + // The jq assertion and its diagnostic must not be able to disagree. + assert.equal(step.split('--argjson drainingOk "${PREDECESSOR_DRAINING_OK}"').length, 3) + assert.doesNotMatch(step, /drainingOk "\$\(test/) + // The fresh VM is still required not to be draining, on every path. + const after = workflow + .split('name: Verify new incarnation, exact image, protocol, and durable safety')[1] + .split('\n - name:')[0] + assert.match(after, /--admission migration-only --draining forbidden/) + const restore = workflow + .split('name: Restore only the verified selected cell to its entry admission')[1] + .split('\n - id:')[0] + assert.match(restore, /--draining forbidden --activity allowed/) + }) + it('leaves the US-only capacity job on the default allowlist', () => { assert.doesNotMatch(capacityWorkflow, /--approved-cells/) }) diff --git a/cloud/docs/relay-workflows.md b/cloud/docs/relay-workflows.md index 4c358c7e0ae..197e9b0cc81 100644 --- a/cloud/docs/relay-workflows.md +++ b/cloud/docs/relay-workflows.md @@ -392,6 +392,12 @@ and the plan validator pins the exact reviewed identity instead of comparing tha roll can never drop or rewrite it. Any other stale line still fails closed and needs a convergence apply first. +A drained cell is refused before a roll, because draining means something is already +shedding its connections. A migration-only cell has none to shed, so the flag decides nothing +there and is accepted on entry; the replacement VM is still required not to be draining, and +the incarnation check still proves it was replaced. That also unwedges the state a failed +canary leaves behind, where the wave's own drain set the flag and no restart followed. + C17 and C18 hold no hosts and are not general, so rolling one displaces nobody: they are the zero-displacement canary for a new image. Their wave enters and leaves migration-only, so its isolate and its restore are both no-ops and the selector generation does not move; a general From 6c913a917f88526c0264f904ba3a51998a573727 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 21:38:09 -0400 Subject: [PATCH 038/168] fix(relay-ops): roll a cell a wave stranded after its drain (#21321) * fix(relay-ops): roll a cell a wave stranded after its drain A wave that stops any time after its drain leaves the cell migration-only and draining on the rollback image, and nothing clears it: the drain flag is a one-way latch on the running process, and the failsafe restarts nothing. Both recovery modes then refuse the cell. Apply wants it general and not draining. Rollback sees the rollback image, reads it as a resume, refuses the draining, and would not have restarted it anyway. The image alone cannot separate a rollback that failed after its template apply from a wave that stopped before one. The restart can: the first left a fresh process, the second did not. Classify on that, so the cell that never restarted takes the rolling path instead of the resuming one. Its template still carries the image it serves, so that is the predecessor its plan is reviewed against, and a template already moved on to the target is refused rather than rolled backwards under a stale review. When the reviewed template is already in place the plan changes nothing, so the MIG is rolled explicitly on the same replacement policy a template change uses; the existing incarnation check is what proves the instance came back. Every other combination of mode, live image, and drain flag keeps the value it had, held by a census that runs the real block over all nine. * fix(relay-ops): pin the replacement method on the explicit MIG roll gcloud persists every rolling-action bound into the group's update policy, and it defaults the replacement method to substitute on a group with no stateful config. Passing surge and unavailable without the method would patch the policy off the declared RECREATE, and the next targeted plan would then carry a MIG change outside version.0.instance_template, which the plan validator refuses. Pass all three so the patch is identical to the declared policy, and read the declared values in the census instead of restating two of them. Dropping the flag, or moving any of the three in Terraform, now fails the census. --- ...d-deploy-relay-production-same-cap-job.yml | 60 ++++++-- .../relay-same-cap-script-census.test.mjs | 133 ++++++++++++++++++ cloud/docs/relay-workflows.md | 38 +++++ 3 files changed, 222 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml b/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml index 2e1bf65d892..617c852d304 100644 --- a/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml +++ b/.github/workflows/cloud-deploy-relay-production-same-cap-job.yml @@ -360,20 +360,41 @@ jobs: } CURRENT_RUNTIME="$(admin_post current-runtime \ "${CELL_ORIGIN}/v1/admin/runtime-status" '{"v":1}')" - # A rollback that failed between template apply and admission restore - # leaves the cell already on the rollback image; resume from that - # state instead of demanding the pre-rollback predecessor. + # Two different failures leave the cell on the rollback image, and the image + # alone cannot tell them apart. A rollback that failed between its template + # apply and its admission restore restarted the cell, so that cell is not + # draining and resumes. A wave that stopped after its drain and before its + # template apply never restarted anything, so its cell is still draining and + # is stranded: the drain flag only clears on a restart, so it has to be rolled. LIVE_IMAGE_DIGEST="$(jq -r '.imageDigest' <<< "${CURRENT_RUNTIME}")" + LIVE_DRAINING="$(jq -r '.draining' <<< "${CURRENT_RUNTIME}")" if test "${DEPLOY_MODE}" = rollback \ && test "${LIVE_IMAGE_DIGEST}" = "${DESIRED_IMAGE_DIGEST}"; then - ROLLBACK_RESUME=true + if test "${LIVE_DRAINING}" = true; then + ROLLBACK_STAGE=stranded + else + ROLLBACK_STAGE=resume + fi PREDECESSOR_IMAGE_DIGEST="${DESIRED_IMAGE_DIGEST}" PREDECESSOR_REHOME_PROTOCOL="${DESIRED_REHOME_PROTOCOL}" else - ROLLBACK_RESUME=false + ROLLBACK_STAGE=roll PREDECESSOR_IMAGE_DIGEST="${CURRENT_IMAGE_DIGEST}" PREDECESSOR_REHOME_PROTOCOL="${CURRENT_REHOME_PROTOCOL}" fi + if test "${ROLLBACK_STAGE}" = resume; then + ROLLBACK_RESUME=true + else + ROLLBACK_RESUME=false + fi + # A stranded cell's template still carries the image the cell is serving, so that + # is the predecessor its plan is reviewed against. A template already moved on to + # the target is refused here rather than rolled backwards under a stale review. + if test "${ROLLBACK_STAGE}" = stranded; then + PLAN_ROLLBACK_IMAGE="${DESIRED_IMAGE}" + else + PLAN_ROLLBACK_IMAGE="${IMAGE_REPOSITORY}@${CURRENT_IMAGE_DIGEST}" + fi # Rollback is the documented recovery from a failed canary, which # leaves the cell migration-only (and possibly still marked # draining); apply and verify still require the cell pristine in the @@ -427,6 +448,11 @@ jobs: fi { echo "ROLLBACK_RESUME=${ROLLBACK_RESUME}" + echo "ROLLBACK_STAGE=${ROLLBACK_STAGE}" + echo "PLAN_ROLLBACK_IMAGE=${PLAN_ROLLBACK_IMAGE}" + # The drain wait and the plan review both read the image this cell actually + # serves, which is the rollback image on a stranded cell and not the current one. + echo "PREDECESSOR_IMAGE_DIGEST=${PREDECESSOR_IMAGE_DIGEST}" # The failsafe consumes these; deriving them here keeps them # defined for a failure in any later step. echo "ISOLATED_MIGRATION_CELLS=${ISOLATED_MIGRATION_CELLS}" @@ -539,7 +565,7 @@ jobs: --cell-id "${TARGET_CELL_ID}" --hard-cap "${EXPECTED_HARD_CAP}" \ --unobserved-bound "${EXPECTED_UNOBSERVED_BOUND}" \ --heartbeat either --admission migration-only --draining required \ - --activity restart-safe --expected-image-digests "${CURRENT_IMAGE_DIGEST}" \ + --activity restart-safe --expected-image-digests "${PREDECESSOR_IMAGE_DIGEST}" \ --timeout-ms 1020000 - id: capacity-auth @@ -627,21 +653,37 @@ jobs: "-target=google_compute_instance_template.relay_gce_cell[\"${TARGET_CELL_ID}\"]" \ "-target=google_compute_instance_group_manager.relay_gce_cell[\"${TARGET_CELL_ID}\"]" \ -out="${RUNNER_TEMP}/relay-same-cap.tfplan" - terraform -chdir=infra/terraform show -json "${RUNNER_TEMP}/relay-same-cap.tfplan" \ + PLAN_REVIEW="$(terraform -chdir=infra/terraform show -json \ + "${RUNNER_TEMP}/relay-same-cap.tfplan" \ | node dev/scripts/validate-relay-capacity-plan.mjs \ --mode same-cap-cell --cell-id "${TARGET_CELL_ID}" \ --hard-cap "${EXPECTED_HARD_CAP}" \ --unobserved-bound "${EXPECTED_UNOBSERVED_BOUND}" --image "${DESIRED_IMAGE}" \ - --rollback-image "${IMAGE_REPOSITORY}@${CURRENT_IMAGE_DIGEST}" \ + --rollback-image "${PLAN_ROLLBACK_IMAGE}" \ --capacity-service-account "${CAPACITY_SERVICE_ACCOUNT}" \ --rehome-director-service-account "${DIRECTOR_RUNTIME_SERVICE_ACCOUNT}" \ --rehome-audience https://relay.onorca.dev/v1/admin/host-drain \ --regional-rehome-protocol "${DESIRED_REHOME_PROTOCOL}" \ - "${POOL_ARGUMENTS[@]}" + "${POOL_ARGUMENTS[@]}")" + echo "${PLAN_REVIEW}" terraform -chdir=infra/terraform apply -auto-approve \ "${RUNNER_TEMP}/relay-same-cap.tfplan" gcloud compute instance-groups managed wait-until "${MIG_NAME}" --stable \ --project "${GCP_PROJECT_ID}" --zone "${TARGET_ZONE}" --timeout 900 + # A stranded cell already runs the reviewed template, so the apply above replaces + # no instance and the drain flag, which only a restart clears, would survive the + # whole wave. Roll the MIG explicitly on exactly the policy a template change uses. + # Every field is passed: gcloud persists these into the MIG's update policy, and it + # defaults the method to substitute on a group with no stateful config, so omitting + # one drifts the policy off the reviewed one and fails every later targeted plan. + if test "${ROLLBACK_STAGE}" = stranded \ + && test "$(jq -er '.changes' <<< "${PLAN_REVIEW}")" = 0; then + gcloud compute instance-groups managed rolling-action replace "${MIG_NAME}" \ + --project "${GCP_PROJECT_ID}" --zone "${TARGET_ZONE}" \ + --replacement-method recreate --max-surge 0 --max-unavailable 1 + gcloud compute instance-groups managed wait-until "${MIG_NAME}" --stable \ + --project "${GCP_PROJECT_ID}" --zone "${TARGET_ZONE}" --timeout 900 + fi - id: post-auth if: ${{ inputs.mode != 'verify' }} diff --git a/cloud/dev/scripts/relay-same-cap-script-census.test.mjs b/cloud/dev/scripts/relay-same-cap-script-census.test.mjs index 399a5cab6cb..5324e7489e7 100644 --- a/cloud/dev/scripts/relay-same-cap-script-census.test.mjs +++ b/cloud/dev/scripts/relay-same-cap-script-census.test.mjs @@ -189,6 +189,44 @@ function drainingBlock() { )}\necho "\${PRECHECK_ADMISSION} \${PRECHECK_DRAINING} \${PREDECESSOR_DRAINING_OK}"` } +// The three fields gcloud would otherwise default, as the MIG resource declares them. +function migUpdatePolicy() { + const terraform = readFileSync( + new URL('../../infra/terraform/relay-gce-cells.tf', import.meta.url), + 'utf8' + ) + const policy = terraform.split(' update_policy {')[1]?.split('\n }')[0] ?? '' + const method = /replacement_method\s+= "([A-Z]+)"/.exec(policy)?.[1] + assert.notEqual(method, undefined, 'the MIG declares no replacement method') + // Both fixed bounds come from the topology locals the MIG resource points at. + const surgeLocal = /max_surge_fixed\s+= local\.relay_gce_topology\.(\w+)/.exec(policy)?.[1] + const unavailableLocal = + /max_unavailable_fixed\s+= local\.relay_gce_topology\.(\w+)/.exec(policy)?.[1] + assert.notEqual(surgeLocal, undefined, 'the MIG pins no surge local') + assert.notEqual(unavailableLocal, undefined, 'the MIG pins no unavailable local') + const topology = terraform.split(' relay_gce_topology = {')[1]?.split('\n }')[0] ?? '' + const local = (name) => { + const value = new RegExp(`${name}\\s+= (\\d+)`).exec(topology)?.[1] + assert.notEqual(value, undefined, `the topology locals pin no ${name}`) + return value + } + return { + replacementMethod: method.toLowerCase(), + maxSurge: local(surgeLocal), + maxUnavailable: local(unavailableLocal) + } +} + +// The stage decides the predecessor, the plan's reviewed rollback image, and whether the +// MIG is rolled explicitly, so run the real block rather than restating its rule. +function stageBlock() { + return `${jobBlock( + ' # Two different failures leave the cell on the rollback image, and the image', + ' PLAN_ROLLBACK_IMAGE="${IMAGE_REPOSITORY}@${CURRENT_IMAGE_DIGEST}"\n fi' + )}\necho "\${ROLLBACK_STAGE} \${ROLLBACK_RESUME} \${PREDECESSOR_IMAGE_DIGEST}` + + ` \${PREDECESSOR_REHOME_PROTOCOL} \${PLAN_ROLLBACK_IMAGE}"` +} + function generationBlock() { return `${jobBlock( ' if test "${DEPLOY_MODE}" = verify; then', @@ -575,6 +613,101 @@ describe('same-cap roll scripts accept every same-cap cell', () => { assert.match(restore, /--draining forbidden --activity allowed/) }) + it('classifies every rollback stage from the real block', () => { + const repository = 'us-central1-docker.pkg.dev/onorca-cloud/orca-cloud/relay' + const target = `sha256:${'7'.repeat(64)}` + const rollback = `sha256:${'0'.repeat(64)}` + const stage = (mode, live, draining) => { + // Exactly how the job assigns them: rollback swaps desired and current. + const desired = mode === 'rollback' ? rollback : target + const current = mode === 'rollback' ? target : rollback + const resolved = spawnSync('bash', ['-euo', 'pipefail', '-c', stageBlock()], { + env: { + ...process.env, + DEPLOY_MODE: mode, + CURRENT_RUNTIME: JSON.stringify({ imageDigest: live, draining }), + DESIRED_IMAGE_DIGEST: desired, + CURRENT_IMAGE_DIGEST: current, + DESIRED_IMAGE: `${repository}@${desired}`, + IMAGE_REPOSITORY: repository, + DESIRED_REHOME_PROTOCOL: '1', + CURRENT_REHOME_PROTOCOL: '0' + }, + encoding: 'utf8' + }) + assert.equal(resolved.status, 0, `${mode}/${live}/${draining}: ${resolved.stderr}`) + return resolved.stdout.trim().split(' ') + } + const roll = (current, protocol) => + ['roll', 'false', current, protocol, `${repository}@${current}`] + // Only the last row differs from main: it used to read `resume` and wedge, because the + // resume path refuses a draining cell and never restarts one. + assert.deepEqual(stage('apply', rollback, false), roll(rollback, '0')) + assert.deepEqual(stage('apply', rollback, true), roll(rollback, '0')) + assert.deepEqual(stage('apply', target, false), roll(rollback, '0')) + assert.deepEqual(stage('verify', rollback, false), roll(rollback, '0')) + assert.deepEqual(stage('rollback', target, false), roll(target, '0')) + assert.deepEqual(stage('rollback', target, true), roll(target, '0')) + assert.deepEqual( + stage('rollback', rollback, false), + ['resume', 'true', rollback, '1', `${repository}@${target}`] + ) + assert.deepEqual( + stage('rollback', rollback, true), + ['stranded', 'false', rollback, '1', `${repository}@${rollback}`] + ) + // A runtime that reports no drain flag at all must never read as stranded. + const [missing] = stage('rollback', rollback, null) + assert.equal(missing, 'resume') + }) + + it('rolls the MIG itself when a stranded plan changes nothing', () => { + const apply = workflow + .split('name: Apply only the selected same-cap template and MIG')[1] + .split('\n - id:')[0] + // The plan is reviewed against the image the cell serves, not an assumed predecessor. + assert.match(apply, /--rollback-image "\$\{PLAN_ROLLBACK_IMAGE\}"/) + assert.doesNotMatch(apply, /--rollback-image "\$\{IMAGE_REPOSITORY\}/) + assert.match( + apply, + /test "\$\{ROLLBACK_STAGE\}" = stranded \\\n\s+&& test "\$\(jq -er '\.changes' <<< "\$\{PLAN_REVIEW\}"\)" = 0/ + ) + // gcloud persists all three fields into the MIG's update policy and defaults the + // method to substitute here, so every one has to match what Terraform declares or the + // recovery drifts the policy and the next targeted plan is refused as an unreviewed + // MIG change. Read the declared values rather than restating them. + assert.match(apply, /rolling-action replace "\$\{MIG_NAME\}"/) + const declared = migUpdatePolicy() + assert.deepEqual(declared, { + replacementMethod: 'recreate', + maxSurge: '0', + maxUnavailable: '1' + }) + assert.match( + apply, + new RegExp( + `--replacement-method ${declared.replacementMethod}` + + ` --max-surge ${declared.maxSurge} --max-unavailable ${declared.maxUnavailable}` + ) + ) + // Nothing else may reach the group, and the roll has to be waited on. + assert.equal(apply.split('rolling-action').length, 2) + assert.equal(apply.split('wait-until "${MIG_NAME}" --stable').length, 3) + }) + + it('waits on the image a stranded cell actually serves', () => { + const isolate = workflow + .split('name: Reversibly isolate and drain only the selected cell')[1] + .split('\n - id:')[0] + assert.match(isolate, /--expected-image-digests "\$\{PREDECESSOR_IMAGE_DIGEST\}"/) + // A stranded cell has to come back on a new process, which is what clears the drain. + const after = workflow + .split('name: Verify new incarnation, exact image, protocol, and durable safety')[1] + .split('\n - name:')[0] + assert.match(after, /test "\$\{TARGET_INCARNATION\}" != "\$\{SOURCE_INCARNATION\}"/) + assert.match(after, /if test "\$\{ROLLBACK_RESUME\}" = true; then/) + }) + it('leaves the US-only capacity job on the default allowlist', () => { assert.doesNotMatch(capacityWorkflow, /--approved-cells/) }) diff --git a/cloud/docs/relay-workflows.md b/cloud/docs/relay-workflows.md index 197e9b0cc81..d81620b837d 100644 --- a/cloud/docs/relay-workflows.md +++ b/cloud/docs/relay-workflows.md @@ -406,6 +406,44 @@ after the first offsets from a single per-wave delta. Neither cell is a declared source, so its template carries no rehome trust lines and it may roll only at rehome protocol `0`; the job refuses a trusted protocol for it before it plans anything. +### Recovering a wave that died after its drain + +A cell's drain flag is a one-way latch on the running process. Only a restart clears it, and +the failsafe that isolates a failed cell does not restart anything. So a wave that stopped +any time after its drain step leaves the cell migration-only and draining, and it stays that +way until the cell is rolled. + +Read the failed run before dispatching anything. If its log has a +`"event":"relay_production_capacity_canary","mode":"drain"` line for the cell, the cell is +drained. Then read the cell's live runtime image from +`POST https://.relay.onorca.dev/v1/admin/runtime-status`. + +1. **Do not re-dispatch `apply`.** It requires the cell general and not draining, and a + drained cell is neither. It will fail closed at the predecessor check. +2. **Dispatch `rollback`,** with the same `target-image-digest` and `rollback-image-digest` + the failed wave used, the live selector generation, and the live tri-state membership + with the failed cell listed under migration-only. The confirmation is + `ROLL_BACK_RELAY_SAME_CAP `. +3. The job classifies the cell itself and needs no extra input: + - serving the **rollback** image and draining, it is `stranded`. The wave stopped before + or during its template apply. The job re-isolates, re-drains, applies the reviewed + template, and rolls the MIG explicitly if that template was already in place. The cell + comes back on a new instance, so the drain clears, and it is restored to its entry class. + - serving the **target** image, it is `roll`, the ordinary rollback. The template applied + and the instance was replaced. + - serving the **rollback** image and not draining, it is `resume`: a rollback that failed + after its own template apply. Nothing is applied and nothing restarts. +4. Rollback takes exactly one cell per dispatch. Recover the cells one at a time. +5. If the run died inside `wait-until stable`, the MIG is still rolling on its own. Wait for + it to settle and re-read the runtime before dispatching, or the stage will be read off a + state that is about to change. +6. A `stranded` dispatch that fails at plan review means the template already carries the + target image while the old instance is still up. Wait for the MIG to finish replacing it, + then dispatch again; it will classify as `roll`. + +A mutating dispatch still needs a fresh aggregate monitor dry-run unless the break-glass +override below is used. + ### Gate override (break-glass) Every mutating same-cap wave normally consumes a fresh 15-minute aggregate monitor dry-run. From f442a5c484d632345f0c70dba003a3086dd37c84 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 17 Sep 2026 18:54:12 -0700 Subject: [PATCH 039/168] fix(native-chat): hide legacy resume command for structured history (#21282) --- .../right-sidebar/AiVaultSessionRow.tsx | 2 +- .../right-sidebar/AiVaultVirtualRow.test.tsx | 120 ++++++++++++++++++ .../right-sidebar/AiVaultVirtualRow.tsx | 5 +- .../ai-vault-session-launch-actions.ts | 3 + .../ai-vault-session-resume.test.ts | 15 +++ .../right-sidebar/ai-vault-session-resume.ts | 9 +- .../src/lib/ai-vault-session-drag.test.ts | 27 ++++ src/renderer/src/lib/ai-vault-session-drag.ts | 4 +- ...i-vault-session-resume-preparation.test.ts | 12 ++ .../ai-vault-session-resume-preparation.ts | 2 +- 10 files changed, 189 insertions(+), 10 deletions(-) create mode 100644 src/renderer/src/components/right-sidebar/AiVaultVirtualRow.test.tsx diff --git a/src/renderer/src/components/right-sidebar/AiVaultSessionRow.tsx b/src/renderer/src/components/right-sidebar/AiVaultSessionRow.tsx index 5f8771315a4..f9a5a8bf33d 100644 --- a/src/renderer/src/components/right-sidebar/AiVaultSessionRow.tsx +++ b/src/renderer/src/components/right-sidebar/AiVaultSessionRow.tsx @@ -122,7 +122,7 @@ export function VaultSessionRow({ ...(resumeStartup.env ? { env: resumeStartup.env } : {}), ...(resumeStartup.envToDelete ? { envToDelete: resumeStartup.envToDelete } : {}), ...(resumeStartup.launchConfig ? { launchConfig: resumeStartup.launchConfig } : {}), - realHomeStartup: realHomeResumeStartup + ...(session.structuredSession ? {} : { realHomeStartup: realHomeResumeStartup }) }) window.dispatchEvent(new Event(AI_VAULT_SESSION_DRAG_START_EVENT)) }, diff --git a/src/renderer/src/components/right-sidebar/AiVaultVirtualRow.test.tsx b/src/renderer/src/components/right-sidebar/AiVaultVirtualRow.test.tsx new file mode 100644 index 00000000000..b7a23d41e62 --- /dev/null +++ b/src/renderer/src/components/right-sidebar/AiVaultVirtualRow.test.tsx @@ -0,0 +1,120 @@ +// @vitest-environment happy-dom + +import { cleanup, fireEvent, render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { TooltipProvider } from '@/components/ui/tooltip' +import type { AiVaultSession } from '../../../../shared/ai-vault-types' +import { AiVaultVirtualRow } from './AiVaultVirtualRow' + +const cliSession: AiVaultSession = { + id: 'local:codex:session-1:/tmp/session-1.jsonl', + executionHostId: 'local', + agent: 'codex', + sessionId: 'session-1', + title: 'CLI session', + cwd: '/repo', + branch: null, + model: null, + filePath: '/tmp/session-1.jsonl', + codexHome: null, + createdAt: null, + updatedAt: null, + modifiedAt: '2026-09-17T00:00:00.000Z', + messageCount: 1, + totalTokens: 0, + previewMessages: [{ role: 'user', text: 'Fix it', timestamp: null }], + queuedMessageCount: 0, + subagentTranscriptCount: 0, + resumeCommand: 'codex resume session-1', + subagent: null +} + +afterEach(() => { + cleanup() +}) + +function renderSession(session: AiVaultSession) { + const buildResumeStartup = vi.fn(() => ({ command: session.resumeCommand })) + const onCopyResume = vi.fn() + render( + + null} + getSessionLiveState={() => null} + getWorktreeInfo={() => null} + getSessionResumeState={() => ({ + blocked: false, + worktreeId: 'worktree-1', + usesSessionWorktree: false + })} + getSessionResumeActions={() => ({ + worktree: { worktreeId: null, disabled: true }, + newTab: { worktreeId: 'worktree-1', disabled: false } + })} + getSessionResumeInChat={() => ({ available: false, reason: 'already-structured' })} + onToggleGroup={vi.fn()} + onToggleSessionDetails={vi.fn()} + onJumpToOriginalPane={vi.fn()} + onJumpToWorktree={vi.fn()} + onResume={vi.fn()} + onContinueInNewSession={vi.fn()} + onResumeInNewChat={vi.fn()} + onCopyResume={onCopyResume} + onCopyId={vi.fn()} + onCopyPath={vi.fn()} + onOpenLog={vi.fn()} + onRevealLog={vi.fn()} + onOpenCwd={vi.fn()} + onRequestDelete={vi.fn()} + /> + + ) + return { buildResumeStartup, onCopyResume } +} + +describe('AiVaultVirtualRow resume command actions', () => { + it('keeps Copy Resume Command in overflow and context actions for CLI sessions', async () => { + const { buildResumeStartup, onCopyResume } = renderSession(cliSession) + const user = userEvent.setup() + + await user.click(screen.getByTestId('ai-vault-session-more-actions')) + await user.click(await screen.findByRole('menuitem', { name: 'Copy Resume Command' })) + expect(onCopyResume).toHaveBeenCalledExactlyOnceWith(cliSession, 'worktree-1') + + fireEvent.contextMenu(screen.getByText('CLI session')) + await user.click(await screen.findByRole('menuitem', { name: 'Copy Resume Command' })) + expect(onCopyResume).toHaveBeenCalledTimes(2) + expect(buildResumeStartup).toHaveBeenCalledTimes(2) + }) + + it('omits Copy Resume Command and legacy command preparation for native sessions', async () => { + const nativeSession: AiVaultSession = { + ...cliSession, + id: 'local:codex:session-native:/tmp/session-native.jsonl', + sessionId: 'session-native', + title: 'Native session', + structuredSession: { sessionId: 'session-native', workspaceId: 'worktree-1' } + } + const { buildResumeStartup, onCopyResume } = renderSession(nativeSession) + const user = userEvent.setup() + + await user.click(screen.getByTestId('ai-vault-session-more-actions')) + expect(screen.queryByRole('menuitem', { name: 'Copy Resume Command' })).toBeNull() + await user.keyboard('{Escape}') + + fireEvent.contextMenu(screen.getByText('Native session')) + expect(screen.queryByRole('menuitem', { name: 'Copy Resume Command' })).toBeNull() + expect(onCopyResume).not.toHaveBeenCalled() + expect(buildResumeStartup).not.toHaveBeenCalled() + }) +}) diff --git a/src/renderer/src/components/right-sidebar/AiVaultVirtualRow.tsx b/src/renderer/src/components/right-sidebar/AiVaultVirtualRow.tsx index 436f6cc5c3e..82965e20ed7 100644 --- a/src/renderer/src/components/right-sidebar/AiVaultVirtualRow.tsx +++ b/src/renderer/src/components/right-sidebar/AiVaultVirtualRow.tsx @@ -131,8 +131,9 @@ export function AiVaultVirtualRow({ const searchHit = row.type === 'session' ? searchHits?.get(row.session.id) : undefined const searchResumeAllowed = searchHit ? canResumeAiVaultSearchHit(searchHit) : true const searchPathAllowed = searchHit ? hasAiVaultSearchHitPath(searchHit) : true + const usesLegacyResumeCommand = row.type === 'session' && !row.session.structuredSession const resumeStartup = - row.type === 'session' && searchResumeAllowed + row.type === 'session' && searchResumeAllowed && usesLegacyResumeCommand ? buildResumeStartup(row.session, resumeState?.worktreeId) : { command: '' } const visibleResumeActions = @@ -166,7 +167,7 @@ export function AiVaultVirtualRow({ liveState={getSessionLiveState(row.session)} resumeStartup={resumeStartup} realHomeResumeStartup={ - searchResumeAllowed + searchResumeAllowed && usesLegacyResumeCommand ? buildResumeStartup({ ...row.session, codexHome: null }, resumeState?.worktreeId) : resumeStartup } diff --git a/src/renderer/src/components/right-sidebar/ai-vault-session-launch-actions.ts b/src/renderer/src/components/right-sidebar/ai-vault-session-launch-actions.ts index 06f99a81dc2..577b700a98c 100644 --- a/src/renderer/src/components/right-sidebar/ai-vault-session-launch-actions.ts +++ b/src/renderer/src/components/right-sidebar/ai-vault-session-launch-actions.ts @@ -64,6 +64,9 @@ export function useAiVaultSessionLaunchActions({ const copyResumeCommand = useCallback( async (session: AiVaultSession, worktreeId?: string | null): Promise => { + if (session.structuredSession) { + return + } try { const preparedSession = await prepareAiVaultSessionForResume(session) await window.api.ui.writeClipboardText(buildResumeCommand(preparedSession, worktreeId)) diff --git a/src/renderer/src/components/right-sidebar/ai-vault-session-resume.test.ts b/src/renderer/src/components/right-sidebar/ai-vault-session-resume.test.ts index b5d3fe53c72..1c34e23b377 100644 --- a/src/renderer/src/components/right-sidebar/ai-vault-session-resume.test.ts +++ b/src/renderer/src/components/right-sidebar/ai-vault-session-resume.test.ts @@ -592,6 +592,21 @@ describe('aiVaultSessionRowResumeGating', () => { }) }) + it('withholds copy-resume from a native structured session', () => { + expect( + aiVaultSessionRowResumeGating( + { + ...sessionWithTurns, + structuredSession: { sessionId: 'session-1', workspaceId: 'worktree-1' } + }, + unblocked + ) + ).toEqual({ + resumeDisabled: false, + canCopyResumeCommand: false + }) + }) + it('treats user/assistant previews as resumable content when the turn count is unknown', () => { const previewOnlySession = { messageCount: 0, diff --git a/src/renderer/src/components/right-sidebar/ai-vault-session-resume.ts b/src/renderer/src/components/right-sidebar/ai-vault-session-resume.ts index 1150f1ef2d6..f029d896e98 100644 --- a/src/renderer/src/components/right-sidebar/ai-vault-session-resume.ts +++ b/src/renderer/src/components/right-sidebar/ai-vault-session-resume.ts @@ -231,17 +231,16 @@ function resolveAiVaultResumeTargetState(args: { } // Resume needs actual conversation content: a zero-turn transcript would resume -// into an empty session. Workspace-target blocking only disables in-app resume; -// copying the command stays available for blocked-but-real sessions, so the copy -// affordance is gated on content alone. +// into an empty session. Copy stays available for blocked CLI sessions, while a +// structured owner reopens natively and must never expose a legacy command. export function aiVaultSessionRowResumeGating( - session: Pick, + session: Pick, state: Pick | null ): { resumeDisabled: boolean; canCopyResumeCommand: boolean } { const hasResumableContent = isAiVaultSessionResumableContent(session) return { resumeDisabled: (state?.blocked ?? true) || !hasResumableContent, - canCopyResumeCommand: hasResumableContent + canCopyResumeCommand: hasResumableContent && !session.structuredSession } } diff --git a/src/renderer/src/lib/ai-vault-session-drag.test.ts b/src/renderer/src/lib/ai-vault-session-drag.test.ts index f8af5c5a84f..ed0326eb38b 100644 --- a/src/renderer/src/lib/ai-vault-session-drag.test.ts +++ b/src/renderer/src/lib/ai-vault-session-drag.test.ts @@ -91,6 +91,33 @@ describe('Session History session drag data', () => { expect(read && 'sessionCwd' in read).toBe(true) }) + it('round-trips a structured session without a legacy resume command', () => { + const transfer = createTransfer() + const payload: AiVaultSessionDragPayload = { + agent: 'codex', + sessionId: 'session-structured', + structuredSession: { sessionId: 'session-structured', workspaceId: 'worktree-1' }, + title: 'Native chat', + command: '' + } + + writeAiVaultSessionDragData(transfer, payload) + + expect(readAiVaultSessionDragData(transfer)).toEqual(payload) + }) + + it('still rejects a blank resume command for an ordinary CLI session', () => { + const transfer = createTransfer() + writeAiVaultSessionDragData(transfer, { + agent: 'codex', + sessionId: 'session-cli', + title: 'CLI session', + command: '' + }) + + expect(readAiVaultSessionDragData(transfer)).toBeNull() + }) + it('keeps sessionCwd absent when an older serializer omitted it', () => { const transfer = createTransfer() transfer.setData( diff --git a/src/renderer/src/lib/ai-vault-session-drag.ts b/src/renderer/src/lib/ai-vault-session-drag.ts index 536616fdf81..af21ab48618 100644 --- a/src/renderer/src/lib/ai-vault-session-drag.ts +++ b/src/renderer/src/lib/ai-vault-session-drag.ts @@ -100,7 +100,9 @@ function isSerializedPayload(value: unknown): value is SerializedAiVaultSessionD isNonEmptyString(payload.sessionId) && (payload.structuredSession === undefined || isStructuredSession(payload.structuredSession)) && isNonEmptyString(payload.title) && - isNonEmptyString(payload.command) && + (payload.structuredSession + ? typeof payload.command === 'string' + : isNonEmptyString(payload.command)) && (payload.sessionFilePath === undefined || isNonEmptyString(payload.sessionFilePath)) && (payload.sessionExecutionHostId === undefined || Boolean(normalizeExecutionHostId(payload.sessionExecutionHostId))) && diff --git a/src/renderer/src/lib/ai-vault-session-resume-preparation.test.ts b/src/renderer/src/lib/ai-vault-session-resume-preparation.test.ts index 1a26b8b80b7..53842d9b749 100644 --- a/src/renderer/src/lib/ai-vault-session-resume-preparation.test.ts +++ b/src/renderer/src/lib/ai-vault-session-resume-preparation.test.ts @@ -43,6 +43,18 @@ describe('prepareAiVaultSessionForResume', () => { expect(prepareSessionResume).not.toHaveBeenCalled() }) + it('does not prepare a legacy CLI resume for a native structured session', async () => { + const prepareSessionResume = vi.fn() + stubPreparation(prepareSessionResume) + const native = session({ + codexHome: '/tmp/orca/codex-runtime-home/home', + structuredSession: { sessionId: 'session-1', workspaceId: 'worktree-1' } + }) + + await expect(prepareAiVaultSessionForResume(native)).resolves.toBe(native) + expect(prepareSessionResume).not.toHaveBeenCalled() + }) + it('repins a per-account session to the home the host substitutes', async () => { const prepareSessionResume = vi.fn().mockResolvedValue({ useRealCodexHome: false, diff --git a/src/renderer/src/lib/ai-vault-session-resume-preparation.ts b/src/renderer/src/lib/ai-vault-session-resume-preparation.ts index 9c7fa8740ff..da2514dd38a 100644 --- a/src/renderer/src/lib/ai-vault-session-resume-preparation.ts +++ b/src/renderer/src/lib/ai-vault-session-resume-preparation.ts @@ -8,7 +8,7 @@ import { LOCAL_EXECUTION_HOST_ID } from '../../../shared/execution-host' export async function prepareAiVaultSessionForResume( session: AiVaultSession ): Promise { - if (!session.structuredSession && !aiVaultSessionNeedsResumePreparation(session)) { + if (session.structuredSession || !aiVaultSessionNeedsResumePreparation(session)) { return session } const result = await window.api.aiVault.prepareSessionResume({ From f2ca3cbfb7bdff0b7bf82a35a83fc49f13d1c857 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 22:08:18 -0400 Subject: [PATCH 040/168] feat(mobile-web-bundle): manifest and RPC contract for the desktop-served mobile web bundle (OTA phase A, 1/5) (#21325) * feat(mobile-web-bundle): add the manifest contract and content-addressed build id The schema every later Phase A lane parses against: the ceilings that bound host memory (256 assets, 32 MiB total, 10 MiB per asset), and a build id that is a pure function of content so a client can use it as a cache key unconditionally. The serializer sorts its input rather than trusting the caller, so a producer that emits assets in any order still lands on the same id. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile-web-bundle): add the bundle RPC payload contract Method names, capability name, the 48 KiB chunk size, params/result schemas for both methods, and the six error codes as a closed union pinned by a coverage record. Constants and data only; the host wiring and the capability push land in later lanes. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web-bundle): hash the build id without node:crypto Metro ships no Node core shims, so a value import from these modules would fail to bundle on the phone. The pure-JS sha256 keeps both contract modules runtime-neutral, which also lets a cached manifest be re-verified on device. Verified digest parity against node:crypto across the 55/56/64-byte padding boundaries before the swap. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web-bundle): reject a manifest whose buildId is not its content hash A stale id passed every other check and would then serve the wrong bytes under a cache key the client already trusts. Runs last of the invariants because it is the only one that hashes. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web-bundle): require a lowercase content type The pattern carried an `i` flag over lowercase character classes, so the same bytes described as `Text/HTML` and `text/html` produced two different build ids. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-web-bundle): move the capability name to a zod-free module A4 wires this constant into protocol-version.ts, which the phone reads on the capability path. Leaving it in the schema module would have dragged zod along with it. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-web-bundle): name the chunk reply's length assetByteLength It is the whole asset's length, not the chunk's, and sitting beside dataBase64 under the old name it read as the chunk's. Both are non-negative integers, so a producer that emitted the wrong one would only surface at the final hash check. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web-bundle): reject asset paths that are not portable or that fold together Two paths differing only in case are one file on macOS and Windows, so the host would serve the same bytes under two entries and one of the two hashes could never match. Windows-reserved segment names and trailing dots cannot be written to the bundle root at all. Both follow skill-package-manifest's checks, the folded-path Set and the reserved-segment pattern. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web-bundle): accept one spelling of a parameterised content type The optional space in `; ?charset=` let the same bytes carry two content types and therefore two build ids. Pinned to the single-space form the bundle builder emits. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * perf(mobile-web-bundle): stop hashing a manifest a cheaper invariant already rejected zod runs superRefine even after the asset-array ceiling has failed, so a 257 asset manifest was still sorted and hashed. Each invariant now returns on its own issue and the count is checked first, which is what the comment claimed. The tests read the issue paths: an oversized or otherwise invalid manifest with a deliberately wrong buildId reports no buildId issue, while the same wrong id inside the ceiling does. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile-web-bundle): say that the manifest has no additive path `.strict()` plus a literal schemaVersion closes the shape completely, so the version bump is the only way to change it. The phone value-imports this schema, so Phase B must read an unrecognised schemaVersion as a bundle to re-fetch rather than as a parse crash. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- .../bundle-rpc-contract.test.ts | 203 ++++++++++++ .../mobile-web-bundle/bundle-rpc-contract.ts | 79 +++++ .../manifest-contract.test.ts | 292 ++++++++++++++++++ .../mobile-web-bundle/manifest-contract.ts | 185 +++++++++++ .../mobile-web-bundle-capability.ts | 4 + 5 files changed, 763 insertions(+) create mode 100644 src/shared/mobile-web-bundle/bundle-rpc-contract.test.ts create mode 100644 src/shared/mobile-web-bundle/bundle-rpc-contract.ts create mode 100644 src/shared/mobile-web-bundle/manifest-contract.test.ts create mode 100644 src/shared/mobile-web-bundle/manifest-contract.ts create mode 100644 src/shared/mobile-web-bundle/mobile-web-bundle-capability.ts diff --git a/src/shared/mobile-web-bundle/bundle-rpc-contract.test.ts b/src/shared/mobile-web-bundle/bundle-rpc-contract.test.ts new file mode 100644 index 00000000000..4cc356026f3 --- /dev/null +++ b/src/shared/mobile-web-bundle/bundle-rpc-contract.test.ts @@ -0,0 +1,203 @@ +import { describe, expect, it } from 'vitest' +import { sha256 } from '../sha256' +import { + computeMobileWebBundleId, + MOBILE_WEB_BUNDLE_ENTRYPOINT, + MOBILE_WEB_BUNDLE_SCHEMA_VERSION, + type MobileWebBundleAsset +} from './manifest-contract' +import { + MobileWebBundleChunkParamsSchema, + MobileWebBundleChunkResultSchema, + MobileWebBundleErrorCodeSchema, + MobileWebBundleManifestParamsSchema, + MobileWebBundleManifestResultSchema, + MOBILE_WEB_BUNDLE_CHUNK_BYTES, + MOBILE_WEB_BUNDLE_CHUNK_METHOD, + MOBILE_WEB_BUNDLE_ERROR_CODES, + MOBILE_WEB_BUNDLE_MANIFEST_METHOD +} from './bundle-rpc-contract' +import { MOBILE_WEB_BUNDLE_CAPABILITY } from './mobile-web-bundle-capability' + +const BUILD_ID = 'a'.repeat(64) +const MAX_DATA_BASE64_LENGTH = Math.ceil(MOBILE_WEB_BUNDLE_CHUNK_BYTES / 3) * 4 + 8 + +function hexDigest(input: string): string { + return Array.from(sha256(new TextEncoder().encode(input)), (byte) => + byte.toString(16).padStart(2, '0') + ).join('') +} + +const ENTRY_ASSET: MobileWebBundleAsset = { + path: MOBILE_WEB_BUNDLE_ENTRYPOINT, + sha256: hexDigest(MOBILE_WEB_BUNDLE_ENTRYPOINT), + byteLength: 64, + contentType: 'text/html; charset=utf-8' +} + +const VALID_MANIFEST = { + schemaVersion: MOBILE_WEB_BUNDLE_SCHEMA_VERSION, + buildId: computeMobileWebBundleId([ENTRY_ASSET]), + desktopVersion: '1.4.200', + minCompatibleRuntimeProtocolVersion: 3, + runtimeProtocolVersion: 3, + entrypoint: MOBILE_WEB_BUNDLE_ENTRYPOINT, + totalBytes: ENTRY_ASSET.byteLength, + assets: [ENTRY_ASSET] +} + +function chunkResult(overrides: Record = {}): Record { + return { + buildId: BUILD_ID, + path: 'assets/a.js', + offset: 0, + assetByteLength: 1024, + sha256: 'b'.repeat(64), + dataBase64: 'AAAA', + eof: true, + ...overrides + } +} + +describe('names and sizes', () => { + it('pins the wire constants', () => { + expect(MOBILE_WEB_BUNDLE_CHUNK_BYTES).toBe(49152) + expect(MOBILE_WEB_BUNDLE_MANIFEST_METHOD).toBe('mobileWeb.bundle.manifest') + expect(MOBILE_WEB_BUNDLE_CHUNK_METHOD).toBe('mobileWeb.bundle.chunk') + expect(MOBILE_WEB_BUNDLE_CAPABILITY).toBe('mobileWeb.bundle.v1') + }) +}) + +describe('MobileWebBundleErrorCodeSchema', () => { + it('round-trips every code', () => { + expect(MOBILE_WEB_BUNDLE_ERROR_CODES).toHaveLength(6) + for (const code of MOBILE_WEB_BUNDLE_ERROR_CODES) { + expect(MobileWebBundleErrorCodeSchema.parse(code)).toBe(code) + } + }) + + it('is closed', () => { + expect(MobileWebBundleErrorCodeSchema.safeParse('mobile_web_bundle_unknown').success).toBe( + false + ) + expect(MobileWebBundleErrorCodeSchema.safeParse('').success).toBe(false) + }) +}) + +describe('mobileWeb.bundle.manifest payloads', () => { + it('takes null params', () => { + expect(MobileWebBundleManifestParamsSchema.safeParse(null).success).toBe(true) + expect(MobileWebBundleManifestParamsSchema.safeParse({}).success).toBe(false) + }) + + it('carries a parsed manifest and the advertised chunk size', () => { + const reply = { manifest: VALID_MANIFEST, chunkBytes: MOBILE_WEB_BUNDLE_CHUNK_BYTES } + const parsed = MobileWebBundleManifestResultSchema.safeParse(reply) + expect(parsed.success).toBe(true) + expect(parsed.success && parsed.data.manifest.buildId).toBe(VALID_MANIFEST.buildId) + expect(MobileWebBundleManifestResultSchema.safeParse({ ...reply, manifest: {} }).success).toBe( + false + ) + expect(MobileWebBundleManifestResultSchema.safeParse({ ...reply, extra: 1 }).success).toBe( + false + ) + }) + + it('allows a shrunk chunk size but not one past the constant', () => { + const reply = { manifest: VALID_MANIFEST, chunkBytes: MOBILE_WEB_BUNDLE_CHUNK_BYTES } + expect( + MobileWebBundleManifestResultSchema.safeParse({ ...reply, chunkBytes: 8 * 1024 }).success + ).toBe(true) + expect( + MobileWebBundleManifestResultSchema.safeParse({ + ...reply, + chunkBytes: MOBILE_WEB_BUNDLE_CHUNK_BYTES + 1 + }).success + ).toBe(false) + expect(MobileWebBundleManifestResultSchema.safeParse({ ...reply, chunkBytes: 0 }).success).toBe( + false + ) + }) +}) + +describe('mobileWeb.bundle.chunk params', () => { + const params = { buildId: BUILD_ID, path: 'assets/a.js', offset: 0 } + + it('accepts a well-formed request', () => { + expect(MobileWebBundleChunkParamsSchema.safeParse(params).success).toBe(true) + }) + + it('is strict and bounded', () => { + expect(MobileWebBundleChunkParamsSchema.safeParse({ ...params, gzip: true }).success).toBe( + false + ) + expect(MobileWebBundleChunkParamsSchema.safeParse({ ...params, offset: -1 }).success).toBe( + false + ) + expect(MobileWebBundleChunkParamsSchema.safeParse({ ...params, offset: 1.5 }).success).toBe( + false + ) + expect( + MobileWebBundleChunkParamsSchema.safeParse({ ...params, path: '../escape.js' }).success + ).toBe(false) + expect(MobileWebBundleChunkParamsSchema.safeParse({ ...params, buildId: 'abc' }).success).toBe( + false + ) + }) + + it('does not pin offset to the constant chunk size, so the host may shrink it', () => { + expect(MobileWebBundleChunkParamsSchema.safeParse({ ...params, offset: 1024 }).success).toBe( + true + ) + }) +}) + +describe('mobileWeb.bundle.chunk result', () => { + it('accepts a self-describing chunk', () => { + expect(MobileWebBundleChunkResultSchema.safeParse(chunkResult()).success).toBe(true) + }) + + it('accepts base64 of a full chunk and rejects one character past the bound', () => { + const full = Buffer.alloc(MOBILE_WEB_BUNDLE_CHUNK_BYTES).toString('base64') + expect(full.length).toBeLessThanOrEqual(MAX_DATA_BASE64_LENGTH) + expect( + MobileWebBundleChunkResultSchema.safeParse(chunkResult({ dataBase64: full })).success + ).toBe(true) + + const atBound = 'A'.repeat(MAX_DATA_BASE64_LENGTH) + expect( + MobileWebBundleChunkResultSchema.safeParse(chunkResult({ dataBase64: atBound })).success + ).toBe(true) + expect( + MobileWebBundleChunkResultSchema.safeParse(chunkResult({ dataBase64: `${atBound}A` })).success + ).toBe(false) + }) + + it('leaves the padding slack the +8 term buys, so the host enforces the chunk size', () => { + const overshoot = Buffer.alloc(MOBILE_WEB_BUNDLE_CHUNK_BYTES + 1).toString('base64') + expect(overshoot.length).toBeLessThanOrEqual(MAX_DATA_BASE64_LENGTH) + const wellPast = Buffer.alloc(MOBILE_WEB_BUNDLE_CHUNK_BYTES + 64).toString('base64') + expect( + MobileWebBundleChunkResultSchema.safeParse(chunkResult({ dataBase64: wellPast })).success + ).toBe(false) + }) + + it('is strict and requires every echoed field', () => { + expect( + MobileWebBundleChunkResultSchema.safeParse(chunkResult({ contentEncoding: 'gzip' })).success + ).toBe(false) + for (const key of [ + 'buildId', + 'path', + 'offset', + 'assetByteLength', + 'sha256', + 'dataBase64', + 'eof' + ]) { + const partial = chunkResult() + delete partial[key] + expect(MobileWebBundleChunkResultSchema.safeParse(partial).success).toBe(false) + } + }) +}) diff --git a/src/shared/mobile-web-bundle/bundle-rpc-contract.ts b/src/shared/mobile-web-bundle/bundle-rpc-contract.ts new file mode 100644 index 00000000000..08b4aa88a72 --- /dev/null +++ b/src/shared/mobile-web-bundle/bundle-rpc-contract.ts @@ -0,0 +1,79 @@ +import { z } from 'zod' +import { hostUnionArms } from '../zod-salvage' +import { + MobileWebBundleAssetPathSchema, + MobileWebBundleManifestSchema, + MOBILE_WEB_BUNDLE_MAX_ASSET_BYTES +} from './manifest-contract' + +/** 48 KiB survives the compounded ~1.78x expansion (base64 body inside a base64 mobile E2EE reply) + * against the 1 MiB frame ceiling on both the WebSocket and relay transports. */ +export const MOBILE_WEB_BUNDLE_CHUNK_BYTES = 48 * 1024 + +export const MOBILE_WEB_BUNDLE_MANIFEST_METHOD = 'mobileWeb.bundle.manifest' +export const MOBILE_WEB_BUNDLE_CHUNK_METHOD = 'mobileWeb.bundle.chunk' + +const SHA256_PATTERN = /^[a-f0-9]{64}$/ +const MAX_DATA_BASE64_LENGTH = Math.ceil(MOBILE_WEB_BUNDLE_CHUNK_BYTES / 3) * 4 + 8 + +export type MobileWebBundleErrorCode = + | 'mobile_web_bundle_unavailable' + | 'mobile_web_bundle_build_changed' + | 'mobile_web_bundle_asset_unknown' + | 'mobile_web_bundle_asset_changed' + | 'mobile_web_bundle_offset_invalid' + | 'mobile_web_bundle_read_limited' + +/** Coverage record, so tsc fails on an arm added to the union without a schema arm and vice versa. */ +export const MOBILE_WEB_BUNDLE_ERROR_CODES = hostUnionArms({ + mobile_web_bundle_unavailable: true, + mobile_web_bundle_build_changed: true, + mobile_web_bundle_asset_unknown: true, + mobile_web_bundle_asset_changed: true, + mobile_web_bundle_offset_invalid: true, + mobile_web_bundle_read_limited: true +}) + +export const MobileWebBundleErrorCodeSchema = z.enum(MOBILE_WEB_BUNDLE_ERROR_CODES) + +export const MobileWebBundleManifestParamsSchema = z.null() + +export const MobileWebBundleManifestResultSchema = z + .object({ + manifest: MobileWebBundleManifestSchema, + /** Read, never assumed, so the host can shrink it without a client release. Capped at the + * constant because a larger value would overshoot the chunk reply's `dataBase64` bound. */ + chunkBytes: z.number().int().positive().max(MOBILE_WEB_BUNDLE_CHUNK_BYTES) + }) + .strict() + +/** No `multipleOf` pin on `offset`: alignment is against the host's advertised `chunkBytes`, which + * may be smaller than the constant, so the host rejects a misaligned offset instead. */ +export const MobileWebBundleChunkParamsSchema = z + .object({ + buildId: z.string().regex(SHA256_PATTERN), + path: MobileWebBundleAssetPathSchema, + offset: z.number().int().nonnegative().max(MOBILE_WEB_BUNDLE_MAX_ASSET_BYTES) + }) + .strict() + +/** Strict, so a later `contentEncoding` is only a Rule 1 optional-field addition for clients whose + * own reply readers are not strict. */ +export const MobileWebBundleChunkResultSchema = z + .object({ + buildId: z.string().regex(SHA256_PATTERN), + path: MobileWebBundleAssetPathSchema, + offset: z.number().int().nonnegative().max(MOBILE_WEB_BUNDLE_MAX_ASSET_BYTES), + /** The whole asset, not this chunk: named for it so a reassembler cannot misread the two, and + * paired with `sha256` it describes the asset without a second index. */ + assetByteLength: z.number().int().nonnegative().max(MOBILE_WEB_BUNDLE_MAX_ASSET_BYTES), + sha256: z.string().regex(SHA256_PATTERN), + dataBase64: z.string().max(MAX_DATA_BASE64_LENGTH), + eof: z.boolean() + }) + .strict() + +export type MobileWebBundleManifestParams = z.infer +export type MobileWebBundleManifestResult = z.infer +export type MobileWebBundleChunkParams = z.infer +export type MobileWebBundleChunkResult = z.infer diff --git a/src/shared/mobile-web-bundle/manifest-contract.test.ts b/src/shared/mobile-web-bundle/manifest-contract.test.ts new file mode 100644 index 00000000000..350197fa8da --- /dev/null +++ b/src/shared/mobile-web-bundle/manifest-contract.test.ts @@ -0,0 +1,292 @@ +import { describe, expect, it } from 'vitest' +import { sha256 } from '../sha256' +import { + computeMobileWebBundleId, + serializeMobileWebBundleAssets, + MobileWebBundleManifestSchema, + MOBILE_WEB_BUNDLE_ENTRYPOINT, + MOBILE_WEB_BUNDLE_MAX_ASSETS, + MOBILE_WEB_BUNDLE_MAX_ASSET_BYTES, + MOBILE_WEB_BUNDLE_MAX_TOTAL_BYTES, + MOBILE_WEB_BUNDLE_SCHEMA_VERSION, + type MobileWebBundleAsset +} from './manifest-contract' + +function hexDigest(input: string): string { + return Array.from(sha256(new TextEncoder().encode(input)), (byte) => + byte.toString(16).padStart(2, '0') + ).join('') +} + +function asset(path: string, byteLength: number): MobileWebBundleAsset { + return { + path, + sha256: hexDigest(path), + byteLength, + contentType: path.endsWith('.html') ? 'text/html; charset=utf-8' : 'text/javascript' + } +} + +const ENTRY = asset(MOBILE_WEB_BUNDLE_ENTRYPOINT, 64) + +function manifestOf( + assets: readonly MobileWebBundleAsset[], + overrides: Record = {} +): Record { + const sorted = [...assets].sort((left, right) => (left.path < right.path ? -1 : 1)) + return { + schemaVersion: MOBILE_WEB_BUNDLE_SCHEMA_VERSION, + buildId: computeMobileWebBundleId(sorted), + desktopVersion: '1.4.200', + minCompatibleRuntimeProtocolVersion: 3, + runtimeProtocolVersion: 3, + entrypoint: MOBILE_WEB_BUNDLE_ENTRYPOINT, + totalBytes: sorted.reduce((sum, entry) => sum + entry.byteLength, 0), + assets: sorted, + ...overrides + } +} + +function assetsTotalling(count: number, totalBytes: number): MobileWebBundleAsset[] { + const others = Array.from({ length: count - 1 }, (_, index) => + asset(`assets/${String(index).padStart(3, '0')}.js`, 0) + ) + return [{ ...ENTRY, byteLength: totalBytes }, ...others] +} + +describe('serializeMobileWebBundleAssets', () => { + const assets = [ENTRY, asset('assets/a.js', 10), asset('assets/b.js', 20)] + + it('is stable under reordered input', () => { + const reversed = assets.toReversed() + const rotated = [assets[1], assets[2], assets[0]] + expect(serializeMobileWebBundleAssets(reversed)).toBe(serializeMobileWebBundleAssets(assets)) + expect(computeMobileWebBundleId(rotated)).toBe(computeMobileWebBundleId(assets)) + expect(computeMobileWebBundleId(reversed)).toMatch(/^[a-f0-9]{64}$/) + }) + + it('serializes in path order with a fixed key order', () => { + expect(serializeMobileWebBundleAssets(assets.toReversed())).toBe( + JSON.stringify([assets[1], assets[2], assets[0]]) + ) + }) + + it('changes the id when any hashed field changes', () => { + const base = computeMobileWebBundleId(assets) + expect(computeMobileWebBundleId([...assets.slice(1), { ...ENTRY, byteLength: 65 }])).not.toBe( + base + ) + expect( + computeMobileWebBundleId([...assets.slice(1), { ...ENTRY, contentType: 'text/plain' }]) + ).not.toBe(base) + expect(computeMobileWebBundleId(assets.slice(1))).not.toBe(base) + }) +}) + +describe('MobileWebBundleManifestSchema', () => { + it('accepts a well-formed manifest', () => { + expect(MobileWebBundleManifestSchema.safeParse(manifestOf([ENTRY])).success).toBe(true) + }) + + it('rejects an unknown key', () => { + expect( + MobileWebBundleManifestSchema.safeParse(manifestOf([ENTRY], { bridge: {} })).success + ).toBe(false) + }) + + it('rejects another schema version', () => { + expect( + MobileWebBundleManifestSchema.safeParse(manifestOf([ENTRY], { schemaVersion: 2 })).success + ).toBe(false) + }) +}) + +describe('contract ceilings', () => { + it('accepts the asset count ceiling and rejects one past it', () => { + const atCeiling = assetsTotalling(MOBILE_WEB_BUNDLE_MAX_ASSETS, 64) + expect(MobileWebBundleManifestSchema.safeParse(manifestOf(atCeiling)).success).toBe(true) + const overCeiling = [...atCeiling, asset('assets/overflow.js', 0)] + expect(overCeiling).toHaveLength(MOBILE_WEB_BUNDLE_MAX_ASSETS + 1) + expect(MobileWebBundleManifestSchema.safeParse(manifestOf(overCeiling)).success).toBe(false) + }) + + it('accepts the per-asset ceiling and rejects one byte past it', () => { + const atCeiling = [{ ...ENTRY, byteLength: MOBILE_WEB_BUNDLE_MAX_ASSET_BYTES }] + expect(MobileWebBundleManifestSchema.safeParse(manifestOf(atCeiling)).success).toBe(true) + const overCeiling = [{ ...ENTRY, byteLength: MOBILE_WEB_BUNDLE_MAX_ASSET_BYTES + 1 }] + expect(MobileWebBundleManifestSchema.safeParse(manifestOf(overCeiling)).success).toBe(false) + }) + + it('accepts the total ceiling and rejects one byte past it', () => { + const perAsset = MOBILE_WEB_BUNDLE_MAX_TOTAL_BYTES / 4 + const atCeiling = [ + { ...ENTRY, byteLength: perAsset }, + asset('assets/a.js', perAsset), + asset('assets/b.js', perAsset), + asset('assets/c.js', perAsset) + ] + expect(MobileWebBundleManifestSchema.safeParse(manifestOf(atCeiling)).success).toBe(true) + const overCeiling = [...atCeiling.slice(1), { ...ENTRY, byteLength: perAsset + 1 }] + expect(MobileWebBundleManifestSchema.safeParse(manifestOf(overCeiling)).success).toBe(false) + }) +}) + +describe('manifest invariants', () => { + const twoAssets = [ENTRY, asset('assets/a.js', 10)] + + it('rejects an unsorted or duplicated asset list', () => { + const reversed = [...twoAssets].sort((left, right) => (left.path < right.path ? 1 : -1)) + expect( + MobileWebBundleManifestSchema.safeParse(manifestOf(twoAssets, { assets: reversed })).success + ).toBe(false) + expect( + MobileWebBundleManifestSchema.safeParse(manifestOf(twoAssets, { assets: [ENTRY, ENTRY] })) + .success + ).toBe(false) + }) + + it('rejects a totalBytes that disagrees with the asset sum', () => { + expect( + MobileWebBundleManifestSchema.safeParse(manifestOf(twoAssets, { totalBytes: 0 })).success + ).toBe(false) + expect( + MobileWebBundleManifestSchema.safeParse(manifestOf(twoAssets, { totalBytes: 75 })).success + ).toBe(false) + }) + + it('rejects a manifest whose entrypoint is not listed', () => { + expect( + MobileWebBundleManifestSchema.safeParse(manifestOf([asset('assets/a.js', 10)])).success + ).toBe(false) + }) + + it('rejects paths that collide when case is folded', () => { + const parsed = MobileWebBundleManifestSchema.safeParse( + manifestOf([ENTRY, asset('assets/A.js', 10), asset('assets/a.js', 10)]) + ) + expect(parsed.success).toBe(false) + // Both sort strictly ascending, so it must be the fold check that fires, not the order check. + expect(parsed.error?.issues.map((issue) => issue.message)).toEqual([ + 'asset paths must not collide when case is folded' + ]) + }) + + it('rejects a buildId that is not the content hash of the assets', () => { + expect( + MobileWebBundleManifestSchema.safeParse(manifestOf(twoAssets, { buildId: 'f'.repeat(64) })) + .success + ).toBe(false) + // A stale id: correct for a previous asset list, so only the recomputation catches it. + expect( + MobileWebBundleManifestSchema.safeParse( + manifestOf(twoAssets, { buildId: computeMobileWebBundleId([ENTRY]) }) + ).success + ).toBe(false) + }) + + it('rejects an inverted protocol window', () => { + expect( + MobileWebBundleManifestSchema.safeParse( + manifestOf(twoAssets, { minCompatibleRuntimeProtocolVersion: 4 }) + ).success + ).toBe(false) + }) +}) + +describe('refinement short-circuit', () => { + const WRONG_BUILD_ID = 'f'.repeat(64) + + function issuePaths(manifest: Record): string[] { + const parsed = MobileWebBundleManifestSchema.safeParse(manifest) + expect(parsed.success).toBe(false) + return (parsed.error?.issues ?? []).map((issue) => issue.path.join('.')) + } + + it('reports buildId when every cheaper invariant holds', () => { + const withinCeiling = assetsTotalling(MOBILE_WEB_BUNDLE_MAX_ASSETS, 64) + expect(issuePaths(manifestOf(withinCeiling, { buildId: WRONG_BUILD_ID }))).toEqual(['buildId']) + }) + + it('does not hash an oversized asset list', () => { + const overCeiling = [ + ...assetsTotalling(MOBILE_WEB_BUNDLE_MAX_ASSETS, 64), + asset('assets/overflow.js', 0) + ] + const paths = issuePaths(manifestOf(overCeiling, { buildId: WRONG_BUILD_ID })) + expect(paths).toEqual(['assets']) + expect(paths).not.toContain('buildId') + }) + + it('does not hash once a cheaper invariant has failed', () => { + const twoAssets = [ENTRY, asset('assets/a.js', 10)] + expect(issuePaths(manifestOf(twoAssets, { totalBytes: 0, buildId: WRONG_BUILD_ID }))).toEqual([ + 'totalBytes' + ]) + expect( + issuePaths( + manifestOf(twoAssets, { + minCompatibleRuntimeProtocolVersion: 4, + buildId: WRONG_BUILD_ID + }) + ) + ).toEqual(['minCompatibleRuntimeProtocolVersion']) + }) +}) + +describe('asset paths', () => { + it.each([ + '../escape.js', + 'assets/../../escape.js', + '/absolute.js', + 'assets\\escape.js', + 'a/./b.js', + 'assets/nul.js', + 'assets/CON', + 'assets/lpt1.js', + 'assets/foo.', + 'assets/...' + ])('rejects %s', (path) => { + const parsed = MobileWebBundleManifestSchema.safeParse( + manifestOf([ENTRY, { ...asset('assets/a.js', 10), path }]) + ) + expect(parsed.success).toBe(false) + }) + + it('accepts exactly one spelling of a parameterised content type', () => { + expect( + MobileWebBundleManifestSchema.safeParse( + manifestOf([{ ...ENTRY, contentType: 'text/html; charset=utf-8' }]) + ).success + ).toBe(true) + // A2's builder emits the single-space form; the other spellings are the same bytes under a + // different build id. + for (const contentType of ['text/html;charset=utf-8', 'text/html; charset=utf-8']) { + expect( + MobileWebBundleManifestSchema.safeParse(manifestOf([{ ...ENTRY, contentType }])).success + ).toBe(false) + } + }) + + it('rejects an uppercase content type, which would give the same bytes two ids', () => { + for (const contentType of ['Text/HTML; charset=utf-8', 'text/JavaScript', 'TEXT/PLAIN']) { + expect( + MobileWebBundleManifestSchema.safeParse( + manifestOf([ENTRY, { ...asset('assets/a.js', 10), contentType }]) + ).success + ).toBe(false) + } + }) + + it('rejects a malformed sha256 or content type', () => { + expect( + MobileWebBundleManifestSchema.safeParse( + manifestOf([ENTRY, { ...asset('assets/a.js', 10), sha256: 'AB'.repeat(32) }]) + ).success + ).toBe(false) + expect( + MobileWebBundleManifestSchema.safeParse( + manifestOf([ENTRY, { ...asset('assets/a.js', 10), contentType: 'nope' }]) + ).success + ).toBe(false) + }) +}) diff --git a/src/shared/mobile-web-bundle/manifest-contract.ts b/src/shared/mobile-web-bundle/manifest-contract.ts new file mode 100644 index 00000000000..9b110d28e64 --- /dev/null +++ b/src/shared/mobile-web-bundle/manifest-contract.ts @@ -0,0 +1,185 @@ +import { z } from 'zod' +import { sha256 } from '../sha256' + +/** A reader that sees another value must reject rather than guess at the shape. */ +export const MOBILE_WEB_BUNDLE_SCHEMA_VERSION = 1 as const + +/** The only stable-named asset, and the only one that references the content-addressed names. */ +export const MOBILE_WEB_BUNDLE_ENTRYPOINT = 'index.html' + +// Permanent contract ceilings. They bound host memory at manifest-read time and never move with the +// per-phase build budget, which lives in the build's own verifier. +export const MOBILE_WEB_BUNDLE_MAX_ASSETS = 256 +export const MOBILE_WEB_BUNDLE_MAX_TOTAL_BYTES = 32 * 1024 * 1024 +export const MOBILE_WEB_BUNDLE_MAX_ASSET_BYTES = 10 * 1024 * 1024 + +const SHA256_PATTERN = /^[a-f0-9]{64}$/ +const ASSET_PATH_PATTERN = /^[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*$/ +const WINDOWS_RESERVED_SEGMENT = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i +// One spelling only, lowercase with a single space before `charset`: content type feeds the build +// id, so every accepted variant of the same type is another id for the same bytes. +const CONTENT_TYPE_PATTERN = /^[a-z0-9][a-z0-9.+-]*\/[a-z0-9][a-z0-9.+-]*(?:; charset=[a-z0-9-]+)?$/ +const MAX_ASSET_PATH_LENGTH = 255 +const MAX_CONTENT_TYPE_LENGTH = 128 +const MAX_DESKTOP_VERSION_LENGTH = 64 + +/** Every segment must be a name the bundle root can hold on all three desktop platforms: no + * traversal, and none of the Windows shapes that cannot be created or that resolve to a device. + * The regex already bans absolute paths, backslashes, spaces, and empty segments. */ +function isPortableAssetSegment(segment: string): boolean { + return ( + segment !== '.' && + segment !== '..' && + !segment.endsWith('.') && + !WINDOWS_RESERVED_SEGMENT.test(segment) + ) +} + +export const MobileWebBundleAssetPathSchema = z + .string() + .max(MAX_ASSET_PATH_LENGTH) + .regex(ASSET_PATH_PATTERN) + .refine( + (path) => path.split('/').every(isPortableAssetSegment), + 'asset path segment must be portable across macOS, Linux, and Windows' + ) + +export const MobileWebBundleAssetSchema = z + .object({ + path: MobileWebBundleAssetPathSchema, + sha256: z.string().regex(SHA256_PATTERN), + byteLength: z.number().int().nonnegative().max(MOBILE_WEB_BUNDLE_MAX_ASSET_BYTES), + contentType: z.string().min(1).max(MAX_CONTENT_TYPE_LENGTH).regex(CONTENT_TYPE_PATTERN) + }) + .strict() + +export type MobileWebBundleAsset = z.infer + +/** Code-unit order, not `localeCompare`: the sort feeds a content hash, so it must not vary. */ +function compareAssetPaths(left: MobileWebBundleAsset, right: MobileWebBundleAsset): number { + if (left.path === right.path) { + return 0 + } + return left.path < right.path ? -1 : 1 +} + +/** The one input to `buildId`: assets sorted by path, fixed key order, no whitespace. Sorting here + * rather than requiring it of the caller is what makes the id a pure function of content. */ +export function serializeMobileWebBundleAssets(assets: readonly MobileWebBundleAsset[]): string { + return JSON.stringify( + [...assets].sort(compareAssetPaths).map((asset) => ({ + path: asset.path, + sha256: asset.sha256, + byteLength: asset.byteLength, + contentType: asset.contentType + })) + ) +} + +/** Pure-JS sha256 rather than `node:crypto`: Metro ships no Node core shims, so the phone must be + * able to recompute the id from a manifest it cached. */ +export function computeMobileWebBundleId(assets: readonly MobileWebBundleAsset[]): string { + const digest = sha256(new TextEncoder().encode(serializeMobileWebBundleAssets(assets))) + return Array.from(digest, (byte) => byte.toString(16).padStart(2, '0')).join('') +} + +function validateManifestInvariants( + manifest: { + buildId: string + entrypoint: string + totalBytes: number + minCompatibleRuntimeProtocolVersion: number + runtimeProtocolVersion: number + assets: readonly MobileWebBundleAsset[] + }, + context: z.RefinementCtx +): void { + // Cheapest first, and each check returns: the build id below is the only one that hashes, and + // zod runs this refinement even when the array ceiling has already failed. + if (manifest.assets.length > MOBILE_WEB_BUNDLE_MAX_ASSETS) { + return + } + let previousPath: string | null = null + let summedBytes = 0 + const foldedPaths = new Set() + for (const asset of manifest.assets) { + if (previousPath !== null && asset.path <= previousPath) { + context.addIssue({ + code: 'custom', + path: ['assets'], + message: 'assets must be sorted by path and unique' + }) + return + } + // Two paths differing only in case are one file on macOS and Windows, so the host would serve + // the same bytes under two entries and one of the two hashes would never match. + const folded = asset.path.toLocaleLowerCase('en-US') + if (foldedPaths.has(folded)) { + context.addIssue({ + code: 'custom', + path: ['assets'], + message: 'asset paths must not collide when case is folded' + }) + return + } + foldedPaths.add(folded) + previousPath = asset.path + summedBytes += asset.byteLength + } + // Without this the total ceiling bounds nothing: a manifest could declare totalBytes 0 and still + // list 256 assets of 10 MiB each. + if (summedBytes !== manifest.totalBytes) { + context.addIssue({ + code: 'custom', + path: ['totalBytes'], + message: 'totalBytes must equal the sum of asset byte lengths' + }) + return + } + if (!manifest.assets.some((asset) => asset.path === manifest.entrypoint)) { + context.addIssue({ + code: 'custom', + path: ['entrypoint'], + message: 'entrypoint must be one of the listed assets' + }) + return + } + if (manifest.minCompatibleRuntimeProtocolVersion > manifest.runtimeProtocolVersion) { + context.addIssue({ + code: 'custom', + path: ['minCompatibleRuntimeProtocolVersion'], + message: 'protocol window must not be inverted' + }) + return + } + // A stale id survives every other check and would then serve the wrong bytes under a cache key + // the client already trusts. + if (manifest.buildId !== computeMobileWebBundleId(manifest.assets)) { + context.addIssue({ + code: 'custom', + path: ['buildId'], + message: 'buildId must be the content hash of the asset list' + }) + } +} + +/** Closed in both directions: `.strict()` rejects an unknown key and `schemaVersion` is a literal, + * so there is no additive path here. Any manifest change is a `schemaVersion` bump, and a phone + * reading a bundle it cached must treat an unrecognised `schemaVersion` as an unusable bundle to + * re-fetch, never as a crash. */ +export const MobileWebBundleManifestSchema = z + .object({ + schemaVersion: z.literal(MOBILE_WEB_BUNDLE_SCHEMA_VERSION), + buildId: z.string().regex(SHA256_PATTERN), + /** The app version that produced the bundle; the update wall's only honest age source. */ + desktopVersion: z.string().min(1).max(MAX_DESKTOP_VERSION_LENGTH), + minCompatibleRuntimeProtocolVersion: z.number().int().nonnegative(), + runtimeProtocolVersion: z.number().int().nonnegative(), + entrypoint: z.literal(MOBILE_WEB_BUNDLE_ENTRYPOINT), + totalBytes: z.number().int().nonnegative().max(MOBILE_WEB_BUNDLE_MAX_TOTAL_BYTES), + assets: z.array(MobileWebBundleAssetSchema).min(1).max(MOBILE_WEB_BUNDLE_MAX_ASSETS) + }) + .strict() + .superRefine(validateManifestInvariants) + +export type MobileWebBundleManifest = z.infer diff --git a/src/shared/mobile-web-bundle/mobile-web-bundle-capability.ts b/src/shared/mobile-web-bundle/mobile-web-bundle-capability.ts new file mode 100644 index 00000000000..bf8c250d224 --- /dev/null +++ b/src/shared/mobile-web-bundle/mobile-web-bundle-capability.ts @@ -0,0 +1,4 @@ +/** Negotiated, never inferred from the desktop version: a build can ship without a bundle. + * Zod-free and dependency-free so `protocol-version.ts` can name it without pulling a schema + * library into the phone's capability path. */ +export const MOBILE_WEB_BUNDLE_CAPABILITY = 'mobileWeb.bundle.v1' From ad4f26cdd4aefa9f8504ba36b84cbade0ee4e39f Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 22:41:13 -0400 Subject: [PATCH 041/168] feat(build): build, verify and package the mobile web bundle with every desktop release (OTA phase A, 2/5) (#21326) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(mobile-web): add the Phase A bootstrap web source A peer of src/ so the root workspace owns it and mobile's separate lockfile stays out of packaging. Four assets across four content types, enough to exercise multi-asset manifest handling rather than assume it. The page reads buildId from manifest.json at runtime: buildId hashes the asset list that index.html belongs to, so injecting it into a hashed asset would make that asset's hash depend on itself. Registered as a fourth typecheck project; without it the entry would be the only TypeScript in a release path that tsc never sees. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(build): build and verify the mobile web bundle from the root workspace Root esbuild over mobile-web/ into out/mobile-web/, content-addressed as assets/. with index.html the only stable name. buildId is the sha256 of the canonical serialization of the sorted asset list, so it is a pure function of content and usable as a cache key with no further reasoning. The verifier builds twice into scratch dirs and compares: a timestamp, an absolute path, or an unstable ordering fails the build when someone introduces it, not the first time a phone gets a spurious cache miss. It also enforces the Phase A budget of 16 assets and 256 KiB, separate from the permanent contract ceiling. build:release does not call build:desktop, so build:mobile-web is wired into build:desktop, build:release, and build:release:parallel. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(packaging): fail the release when the mobile web bundle is missing or stale electron-builder only warns about a missing input, so without a beforePack guard a release ships an app that advertises the bundle capability and then errors on every request. The hash check, not the existence check, is what catches a half-written or stale out/. The source tree is excluded from app.asar; out/mobile-web ships inside it under the existing out rules, exactly as out/web does. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-web): narrow the manifest with `in` instead of a cast The changed-code casting gate rejects assertions, and `in` narrows the same untrusted JSON without one. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web): move the bundle source under src/ so the root guard passes .github/scripts/check-root-directory-entries.mjs blocks any new top-level entry by name, so mobile-web/ could not live at the root. The source is excluded from app.asar by the existing '!src{,/**/*}' rule; the explicit '!src/mobile-web{,/**/*}' entry stays as a marker. out/mobile-web is unaffected and still ships under the out rules like out/web. No tsconfig includes src/**, so node, web, cli, and relay do not pick the tree up; it is registered as a knip entry so audit:dead-code does not call it unused. buildId is unchanged at 9d78435e: the builder hashes content, not paths. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(build): resolve the entry-script guard through pathToFileURL `file://${process.argv[1]}` never equals import.meta.url on Windows, where that url is file:///C:/... So the builder exited 0 having written nothing and the Windows packaging job failed later, at the guard, with no clue why. Every other script in config/scripts already uses pathToFileURL; this one now does too, via an exported predicate a posix runner can exercise with a win32 path. The verify script had no entry guard at all, so importing its budget constants ran the whole verification — including its process.exit — inside the test worker. It is now a function behind the same guard. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(ci): build the mobile web bundle in the PR package job That job assembles packaging inputs step by step instead of calling build:release, so the new beforePack guard hard-failed it. The census test added here is the oracle: it walks every workflow job that invokes electron-builder without --prepackaged (which short-circuits doPack before beforePack) and requires a bundle-producing script in the same job. It goes red on exactly pr.yml's package job when this step is removed. Ten jobs covered; the other nine already ran build:release, build:release:parallel, or build:desktop. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web): pin source line endings, because CRLF changes the buildId Every text byte under src/mobile-web is hashed into an asset digest and from there into buildId, so a CRLF checkout produces a different bundle id for the same commit: 91af2897 instead of 9d78435e. That would make a Windows-built desktop disagree with a mac-built one about which bundle a phone has cached. .gitattributes pins eol=lf for the text sources and -text for the PNG, matching the four trees already pinned for byte-hashing. The verify script asserts no source file carries a CR, so the build fails if the pin ever stops applying rather than silently shipping a second bundle identity. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * style(build): read the test's own path from import.meta.filename oxlint unicorn/prefer-import-meta-properties. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(test): census packaging jobs over raw workflow text, not re-serialized YAML yaml.stringify folds long lines, and in dev-channel-win-build.yml's build-win the fold landed between `electron-builder` and `--config`, so a real packaging job was invisible to the census: 11 jobs exist, the test saw 10. Slice each job's raw source by its parsed boundaries instead, and pin the inventory so a new packaging workflow has to be added here on purpose. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(build): assert the script chain the packaging census trusts The census only checks that a packaging job invokes one of ten build scripts; that those scripts still reach build:mobile-web was asserted nowhere, so a dropped link would leave every job looking covered while packaging failed at beforePack. Resolve each script for real, and pin pr.yml's hand-rolled step, since that job never calls build:release. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(build): realpath the entry path before the direct-invocation compare Node resolves symlinks in import.meta.url but not in argv[1], so `node /tmp/...` against a /private/tmp realpath compared two different strings: the builder and the verifier exited 0 having written and checked nothing. Same silent-success shape as the Windows file:// bug, so the fix sits next to it, with both seams injectable. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * style(mobile-web): format bootstrap.css with oxfmt It was the only tracked CSS failing oxfmt --check. The buildId is unchanged at 9d78435e8bb73c3341f833c20aaefbd7bfdfc414b68dadf87c1689d86728fe33, because esbuild's CSS minifier normalises the whitespace this touches before the asset is hashed. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(packaging): reject bundle files the manifest does not list The guard only walked the manifest, so a dropped assets/stale.js passed: assets are content-addressed, nothing ever overwrites a stale copy, and it would ship inside asar unreachable and unverified. Require every file under out/mobile-web to be the manifest or a listed asset. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(packaging): give beforePack an explicit mobile web bundle root The bundle guard read the repo's out/mobile-web unconditionally, so the two arch-aware packaging tests that call the real beforePack went red in the unit-test job, which never runs build:mobile-web. beforePack now takes the bundle root as a second parameter defaulting to out/mobile-web, which is what electron-builder gets, and those tests build a real bundle into a temp dir instead. The guard is neither skipped nor made tolerant of a missing bundle. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(packaging): census sees script-wrapped packers; dev verify reuses the guard The workflow census only matched a literal `electron-builder --config` line, so daemon-relocation-spike's `pnpm run build:unpack` (which packs and runs beforePack) was invisible to it. Jobs now count when any `pnpm run + + diff --git a/src/mobile-web/src/bootstrap.css b/src/mobile-web/src/bootstrap.css new file mode 100644 index 00000000000..3fc2529db97 --- /dev/null +++ b/src/mobile-web/src/bootstrap.css @@ -0,0 +1,54 @@ +:root { + color-scheme: dark light; + --bootstrap-fg: #e6edf3; + --bootstrap-muted: #8b98a5; + --bootstrap-bg: #0d1117; +} + +body { + margin: 0; + background: var(--bootstrap-bg); + color: var(--bootstrap-fg); + font-family: + ui-sans-serif, + system-ui, + -apple-system, + 'Segoe UI', + sans-serif; +} + +.bootstrap { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 12px; + padding: 24px; +} + +.bootstrap__mark { + image-rendering: pixelated; +} + +.bootstrap__title { + margin: 0; + font-size: 18px; + font-weight: 600; +} + +.bootstrap__facts { + display: grid; + grid-template-columns: max-content 1fr; + gap: 4px 12px; + margin: 0; + font-size: 13px; +} + +.bootstrap__facts dt { + color: var(--bootstrap-muted); +} + +.bootstrap__facts dd { + margin: 0; + overflow-wrap: anywhere; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; +} diff --git a/src/mobile-web/src/bootstrap.ts b/src/mobile-web/src/bootstrap.ts new file mode 100644 index 00000000000..805ee5381df --- /dev/null +++ b/src/mobile-web/src/bootstrap.ts @@ -0,0 +1,65 @@ +// Build-time constants, substituted by config/scripts/build-mobile-web-bundle.mjs via esbuild define. +declare const ORCA_MOBILE_WEB_DESKTOP_VERSION: string +declare const ORCA_MOBILE_WEB_RUNTIME_PROTOCOL_VERSION: number +declare const ORCA_MOBILE_WEB_MIN_COMPATIBLE_RUNTIME_PROTOCOL_VERSION: number + +// Why a runtime read and not a define: buildId is the hash of the asset list that index.html +// belongs to, so injecting it into a hashed asset would make the hash depend on itself. +const MANIFEST_URL = './manifest.json' + +function isBuildId(value: unknown): value is string { + return typeof value === 'string' && /^[0-9a-f]{64}$/.test(value) +} + +async function readBuildId(): Promise { + const response = await fetch(MANIFEST_URL, { cache: 'no-store' }) + if (!response.ok) { + throw new Error(`manifest request failed with ${String(response.status)}`) + } + const manifest: unknown = await response.json() + // `in` narrows without an assertion; the manifest is untrusted JSON either way. + if (typeof manifest !== 'object' || manifest === null || !('buildId' in manifest)) { + throw new Error('manifest has no buildId') + } + const { buildId } = manifest + if (!isBuildId(buildId)) { + throw new Error('manifest buildId is not a sha256 digest') + } + return buildId +} + +function renderFacts(facts: readonly (readonly [string, string])[]): void { + const list = document.getElementById('bootstrap-facts') + if (!(list instanceof HTMLDListElement)) { + return + } + list.replaceChildren() + for (const [term, description] of facts) { + const dt = document.createElement('dt') + dt.textContent = term + const dd = document.createElement('dd') + dd.textContent = description + dd.dataset.fact = term + list.append(dt, dd) + } +} + +async function start(): Promise { + let buildId: string + try { + buildId = await readBuildId() + } catch (error) { + buildId = `unavailable (${error instanceof Error ? error.message : String(error)})` + } + renderFacts([ + ['buildId', buildId], + ['desktopVersion', ORCA_MOBILE_WEB_DESKTOP_VERSION], + ['runtimeProtocolVersion', String(ORCA_MOBILE_WEB_RUNTIME_PROTOCOL_VERSION)], + [ + 'minCompatibleRuntimeProtocolVersion', + String(ORCA_MOBILE_WEB_MIN_COMPATIBLE_RUNTIME_PROTOCOL_VERSION) + ] + ]) +} + +void start() diff --git a/src/mobile-web/src/orca-mark.png b/src/mobile-web/src/orca-mark.png new file mode 100644 index 0000000000000000000000000000000000000000..274fd7bf7824726a834ca751c72d4386b7567a06 GIT binary patch literal 105 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`2A(dCAr-fh6C~;y>fdepuVlR; zd4i1N*&_*a*bd|ze9k0bo5i5ZyTC$9)6c_Mn1P{d*3*Y7Hhb;>^)h(6`njxgN@xNA DvG5 Date: Thu, 17 Sep 2026 22:52:49 -0400 Subject: [PATCH 042/168] fix: make worktree scan failures actionable (#21291) * fix: make worktree scan failures actionable * fix: preserve remote worktree scan diagnostics --- .../listing/detected-provider-listing.ts | 8 +- .../rows/RepoScanUnavailableIndicator.tsx | 154 +++++++++++++----- src/renderer/src/i18n/locales/en.json | 7 +- .../listing/detected-worktree-host-merge.ts | 1 + .../detected-worktree-provider-request.ts | 4 +- .../listing/worktree-catalog-visibility.ts | 1 + src/shared/worktree-scan-failure.test.ts | 28 ++++ src/shared/worktree-scan-failure.ts | 25 +++ src/shared/worktree/types.ts | 3 + 9 files changed, 189 insertions(+), 42 deletions(-) create mode 100644 src/shared/worktree-scan-failure.test.ts create mode 100644 src/shared/worktree-scan-failure.ts diff --git a/src/main/ipc/worktrees/listing/detected-provider-listing.ts b/src/main/ipc/worktrees/listing/detected-provider-listing.ts index ec5e7606e45..4feeb42dd58 100644 --- a/src/main/ipc/worktrees/listing/detected-provider-listing.ts +++ b/src/main/ipc/worktrees/listing/detected-provider-listing.ts @@ -31,6 +31,7 @@ import { warnOnce } from './worktree-listing-diagnostics' import { readAllWorktreeMetaForRepo } from '../../../persistence/host-qualified-worktree-meta' +import { classifyWorktreeScanFailure } from '../../../../shared/worktree-scan-failure' export async function listDetectedWorktreesForCapturedRepo( store: Store, @@ -163,6 +164,7 @@ export async function listDetectedWorktreesForCapturedRepo( ) // Why: retention alone leaves inert rows with no explanation; the cause rides with the listing. const unavailableReason = describeWorktreeScanFailure(err) + const failureKind = classifyWorktreeScanFailure(unavailableReason) if (repo.connectionId) { const worktrees = listDisconnectedSshWorktrees(store, repo, sshWorktreeMetaIndex()) return { @@ -170,7 +172,8 @@ export async function listDetectedWorktreesForCapturedRepo( authoritative: false, source: 'metadata-fallback', worktrees: buildDisconnectedDetectedWorktrees(store, repo, worktrees), - unavailableReason + unavailableReason, + failureKind } } return { @@ -178,7 +181,8 @@ export async function listDetectedWorktreesForCapturedRepo( authoritative: false, source: 'metadata-fallback', worktrees: [], - unavailableReason + unavailableReason, + failureKind } } } diff --git a/src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.tsx index a956598140e..bcc08529218 100644 --- a/src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.tsx @@ -1,16 +1,29 @@ import React from 'react' import { TriangleAlert } from 'lucide-react' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' import { cn } from '@/lib/utils' import { translate } from '@/i18n/i18n' import { useAppStore } from '@/store' import type { Repo } from '../../../../../../shared/repo-types' import { getRepoExecutionHostId } from '../../../../../../shared/execution-host' +import { + classifyWorktreeScanFailure, + type WorktreeScanFailureKind +} from '../../../../../../shared/worktree-scan-failure' import { handleRepoHeaderActionPointerDown, stopRepoHeaderKeyboardToggle } from './header-event-guards' +const WORKTREE_SCAN_FIX_COMMANDS = { + 'xcode-license': 'sudo xcodebuild -license', + 'developer-tools': 'xcode-select --install' +} as const satisfies Partial> + +function fixCommandForFailureKind(kind: WorktreeScanFailureKind): string | undefined { + return WORKTREE_SCAN_FIX_COMMANDS[kind] +} + /** * Marks a repo whose worktree scan failed, so its rows are retained but cannot be trusted. * Click re-runs the scan: the failure is otherwise re-tried only by the next incidental refresh. @@ -31,45 +44,110 @@ export function RepoScanUnavailableIndicator({ repo }: { repo: Repo }): React.JS 'auto.components.sidebar.RepoScanUnavailableIndicator.retry', 'Retry scan' ) + const executionHostId = getRepoExecutionHostId(repo) + const isLocalHost = executionHostId === 'local' && !repo.connectionId + const isLocalMac = isLocalHost && navigator.userAgent.includes('Mac') + const failureKind: WorktreeScanFailureKind = + detected.failureKind ?? + (isLocalMac ? classifyWorktreeScanFailure(detected.unavailableReason) : 'unknown') + const failureMessageByKind: Partial> = { + 'xcode-license': translate( + 'auto.components.sidebar.RepoScanUnavailableIndicator.xcodeLicense', + 'Apple developer tools require license acceptance before Git can run.' + ), + 'developer-tools': translate( + 'auto.components.sidebar.RepoScanUnavailableIndicator.developerTools', + 'Apple command-line developer tools are missing or unavailable.' + ), + 'architecture-mismatch': translate( + 'auto.components.sidebar.RepoScanUnavailableIndicator.architectureMismatch', + 'A Git-related executable could not run because its CPU architecture is incompatible with this execution host. Install Git and related tools for the host architecture.' + ) + } + const failureMessage = failureMessageByKind[failureKind] ?? detected.unavailableReason + const fixCommand = isLocalMac ? fixCommandForFailureKind(failureKind) : undefined + const diagnosticText = [ + `Repository: ${repo.displayName}`, + ...(isLocalMac + ? [`Path: ${repo.path}`, 'Client platform: macOS'] + : [`Execution host: ${executionHostId}`]), + `Failure: ${detected.unavailableReason}` + ].join('\n') + const copyText = async (value: string): Promise => { + await window.api.ui.writeClipboardText(value) + } return ( - - - - - -
-
{title}
-
{detected.unavailableReason}
-
- {translate( - 'auto.components.sidebar.RepoScanUnavailableIndicator.retained', - 'Existing worktrees are kept until a scan succeeds. Click to retry.' + + + + + + +
+
{title}
+
{failureMessage}
+ {fixCommand ? ( +
+
+ {fixCommand} +
+
+ ) : null} +
+ {translate( + 'auto.components.sidebar.RepoScanUnavailableIndicator.retained', + 'Existing worktrees are kept until a scan succeeds. Click to retry.' + )} +
+
+ {fixCommand ? ( + + ) : null} + +
-
- - + + + ) } diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index 709597aec8f..00c2bfe2514 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -6356,7 +6356,12 @@ "RepoScanUnavailableIndicator": { "title": "Worktree scan failed for {{value0}}", "retry": "Retry scan", - "retained": "Existing worktrees are kept until a scan succeeds. Click to retry." + "retained": "Existing worktrees are kept until a scan succeeds. Click to retry.", + "xcodeLicense": "Apple developer tools require license acceptance before Git can run.", + "developerTools": "Apple command-line developer tools are missing or unavailable.", + "architectureMismatch": "A Git-related executable could not run because its CPU architecture is incompatible with this execution host. Install Git and related tools for the host architecture.", + "copyCommand": "Copy command", + "copyDiagnostics": "Copy diagnostics" } }, "shared": { diff --git a/src/renderer/src/store/slices/worktrees/listing/detected-worktree-host-merge.ts b/src/renderer/src/store/slices/worktrees/listing/detected-worktree-host-merge.ts index f8078a9925c..b1fb7f7bafb 100644 --- a/src/renderer/src/store/slices/worktrees/listing/detected-worktree-host-merge.ts +++ b/src/renderer/src/store/slices/worktrees/listing/detected-worktree-host-merge.ts @@ -23,6 +23,7 @@ export function mergeDetectedWorktreesForHost( current.authoritative === refreshed.authoritative && current.source === refreshed.source && current.unavailableReason === refreshed.unavailableReason && + current.failureKind === refreshed.failureKind && current.worktrees === worktrees ) { return current diff --git a/src/renderer/src/store/slices/worktrees/listing/detected-worktree-provider-request.ts b/src/renderer/src/store/slices/worktrees/listing/detected-worktree-provider-request.ts index e85add4fc85..4f13b4a6105 100644 --- a/src/renderer/src/store/slices/worktrees/listing/detected-worktree-provider-request.ts +++ b/src/renderer/src/store/slices/worktrees/listing/detected-worktree-provider-request.ts @@ -16,6 +16,7 @@ import type { } from './worktree-slice-types' import { isRuntimeMethodNotFoundError } from './runtime-worktree-rpc-errors' import { toLegacyDetectedWorktreeResult } from './worktree-host-ownership' +import { isWorktreeScanFailureKind } from '../../../../../../shared/worktree-scan-failure' export async function listDetectedWorktreesForRepo( settings: AppState['settings'], @@ -86,7 +87,8 @@ export function isDetectedWorktreeListResult(value: unknown): value is DetectedW (result.source === 'git' || result.source === 'metadata-fallback' || result.source === 'session-fallback') && - Array.isArray(result.worktrees) + Array.isArray(result.worktrees) && + (result.failureKind === undefined || isWorktreeScanFailureKind(result.failureKind)) ) } diff --git a/src/renderer/src/store/slices/worktrees/listing/worktree-catalog-visibility.ts b/src/renderer/src/store/slices/worktrees/listing/worktree-catalog-visibility.ts index c6d3a9636f7..2fcf8d51d28 100644 --- a/src/renderer/src/store/slices/worktrees/listing/worktree-catalog-visibility.ts +++ b/src/renderer/src/store/slices/worktrees/listing/worktree-catalog-visibility.ts @@ -15,6 +15,7 @@ export function areDetectedWorktreeResultsEqual( current.authoritative === next.authoritative && current.source === next.source && current.unavailableReason === next.unavailableReason && + current.failureKind === next.failureKind && catalogRowsEqual(current.worktrees, next.worktrees) ) } diff --git a/src/shared/worktree-scan-failure.test.ts b/src/shared/worktree-scan-failure.test.ts new file mode 100644 index 00000000000..0981344ec4d --- /dev/null +++ b/src/shared/worktree-scan-failure.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from 'vitest' +import { classifyWorktreeScanFailure } from './worktree-scan-failure' + +describe('classifyWorktreeScanFailure', () => { + it('recognizes Xcode license failures', () => { + expect( + classifyWorktreeScanFailure('Agreeing to the Xcode/iOS license requires admin privileges') + ).toBe('xcode-license') + }) + it('recognizes missing developer tools', () => { + expect(classifyWorktreeScanFailure('xcode-select: error: no developer tools were found')).toBe( + 'developer-tools' + ) + }) + it('does not prescribe installation for an unspecified xcode-select path error', () => { + expect(classifyWorktreeScanFailure('xcode-select: error: invalid active developer path')).toBe( + 'unknown' + ) + }) + it('recognizes architecture spawn failures', () => { + expect(classifyWorktreeScanFailure('spawn Unknown system error -86')).toBe( + 'architecture-mismatch' + ) + }) + it('keeps unrecognized failures unknown', () => { + expect(classifyWorktreeScanFailure('git failed for an unspecified reason')).toBe('unknown') + }) +}) diff --git a/src/shared/worktree-scan-failure.ts b/src/shared/worktree-scan-failure.ts new file mode 100644 index 00000000000..80c9d2db810 --- /dev/null +++ b/src/shared/worktree-scan-failure.ts @@ -0,0 +1,25 @@ +export const WORKTREE_SCAN_FAILURE_KINDS = [ + 'xcode-license', + 'developer-tools', + 'architecture-mismatch', + 'unknown' +] as const + +export type WorktreeScanFailureKind = (typeof WORKTREE_SCAN_FAILURE_KINDS)[number] + +export function isWorktreeScanFailureKind(value: unknown): value is WorktreeScanFailureKind { + return WORKTREE_SCAN_FAILURE_KINDS.some((kind) => kind === value) +} + +export function classifyWorktreeScanFailure(reason: string): WorktreeScanFailureKind { + if (/Agreeing to the Xcode\/iOS license requires admin privileges/i.test(reason)) { + return 'xcode-license' + } + if (/no developer tools were found/i.test(reason)) { + return 'developer-tools' + } + if (/Unknown system error -86|EBADARCH|Bad CPU type in executable/i.test(reason)) { + return 'architecture-mismatch' + } + return 'unknown' +} diff --git a/src/shared/worktree/types.ts b/src/shared/worktree/types.ts index e368716dc01..e07696016a9 100644 --- a/src/shared/worktree/types.ts +++ b/src/shared/worktree/types.ts @@ -6,6 +6,7 @@ import type { DiffComment, MobileDiffReviewState } from '../diff-comment-types' import type { EphemeralVmCheckoutMode } from '../orca-yaml-hook-types' import type { BuiltInWorktreeVisibilitySourceId } from '../repo-types' import type { WorktreeIdentity } from './identity' +import type { WorktreeScanFailureKind } from '../worktree-scan-failure' export type WorkspaceLinkedItem = { provider: 'github' | 'gitlab' | 'linear' | 'jira' @@ -223,4 +224,6 @@ export type DetectedWorktreeListResult = { worktrees: DetectedWorktree[] /** Why a non-authoritative listing could not be scanned; additive, older hosts omit it. */ unavailableReason?: string + /** Structured cause captured by the execution host when a scan fails. */ + failureKind?: WorktreeScanFailureKind } From 9c921360090667e02363fe6e4dee98410665427a Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Thu, 17 Sep 2026 22:55:41 -0400 Subject: [PATCH 043/168] fix(relay): probe a half-open control socket instead of waiting out the silence bound (STA-7672) (#21076) * fix(relay): probe a half-open control socket instead of waiting out the silence bound (STA-7672) A socket CLOSE rejects a pending request as relay_control_closed_, so a relay_control_request_timeout is positive proof the socket stayed open and simply never replied. The only thing that reaps such a socket is RELAY_CONTROL_SILENCE_LIMIT_MS = 75_000, combed every 15s, against a 10s request deadline. On Windows behind NAT/VPN or across sleep-resume a half-open TCP socket accepts send() into a dead pipe and stays invisible for 75-90s, so every pairing attempt in that window times out. The reporter burned ~7. A request that times out with no inbound frame since its send now arms an RFC 6455 ping probe. Terminating on the timeout alone was rejected: relay control ops run DB transactions that can outlive the deadline, and the existing comment in handleMessage records that self-closing on a late reply was strictly worse than ignoring it -- it orphaned the relay session and answered the phone with HOST_OFFLINE for minutes. The probe distinguishes the two cases instead of guessing. The probe deadline deliberately exceeds the relay's own 15s application-level ping cadence. Relay liveness never depended on RFC 6455 control frames surviving end to end, so a shorter window would let a middlebox that swallows pongs turn every request timeout into a reconnect loop. At 20s a healthy cell clears the probe either way -- with a pong, or with the ping it was going to send anyway -- so a probe that fires means the pipe carried neither. Detection drops from 75-90s to ~30s. A pong clears a probe but deliberately does not feed the silence watchdog: it proves the pipe, not that the relay still indexes the session. The timeout error also stops being a bare string; it now names the request kind, the cell, the socket age, the time since the last inbound frame, and whether a probe was armed. The silence watchdog, the probe and the socket age now live in one RelayControlLiveness owner rather than scattered across RelayControlClient. * fix(relay): require a run of unanswered probes before tearing down a control A single unanswered probe was treated as proof of a dead pipe. STA-3320 already established that it is not: a cellular/VPN blackhole or a stalled TCP retransmit routinely swallows one pong from a peer that is still there, which is why RemoteRuntimeServerHeartbeat requires three consecutive misses. The networks this detection exists for are exactly the ones that drop a lone frame, so the first cut was more trigger-happy than the rest of the product. Three changes, all aimed at the cost of a false positive rather than the detection itself: - Three consecutive unanswered probes are now required. The interval drops to 8s so the full run (24s) still outlasts the relay's 15s application-level ping, preserving the property that a healthy cell clears the probe even where a middlebox swallows RFC 6455 control frames. Detection lands at ~34s rather than ~30s, against 75-90s before the fix. Any inbound frame retires the whole run, so a later probe never inherits an earlier miss. - The deadline carries the fleet's existing +/-10% jitter (RELAY_RENEWAL_JITTER_RATIO). Without it every host timing out against one slow cell would probe and terminate on the same boundary -- the synchronized cohort burst that constant was introduced for. The pre-existing 75s watchdog comb has the same defect; this path does not add to it. - A liveness teardown now names its cause in the log. It reaches the origin as an ordinary 1006 close, so without a label a probe-driven reconnect is indistinguishable from any other drop, and a fleet-wide false positive would be invisible in exactly the incident where it matters. Mutation-checked: a miss limit of 1 fails four tests, 2 fails one, and removing the jitter fails one. * fix(relay): keep the request-timeout rejection classifiable The diagnostics added in the previous commit were appended to the rejection's message, which silently destroyed the signal they were meant to add. `mobileRelayMintFailureFromUnknown` classifies a relay failure by testing `error.message` against an anchored `/^relay_[a-z0-9_]{1,74}$/`, so `relay_control_request_timeout reqKind=invite cell=...` stopped matching and every pairing timeout was reported as the generic `relay_mint_failed` instead -- in exactly the flow STA-7672 is about. The pairing path logs only the resolved code and discards the rejection's text, so nothing ever surfaced the suffix: the change was a net loss of diagnosis. The message is bare again and the diagnostics are logged from RelayControlLiveness, which is the only place they survive. Added relay-control-timeout-classification.test.ts to pin the contract end to end through the real classifier, since the coupling is invisible at both sites: restoring the suffix turns the assertion into relay_mint_failed. Found in adversarial review. * refactor(relay): collapse the half-open detection onto one object Design review of the three commits on this branch. No behaviour change: the 184 relay tests pass unmodified, and reverting PROBE_MISS_LIMIT to 1 or 2, or dropping the jitter, still fails them. Dead plumbing. `probeIntervalMs` had zero callers across three layers (client options -> conditional spread -> liveness default), and `silenceLimitMs` the same -- the only production construction site, relay-control-origin.ts, passes neither. Both are gone. `livenessRandom` stays; one test uses it. The conditional-spread idiom went with them: `exactOptionalPropertyTypes` is off for src/ (only cloud/apps/relay-ops sets it), so it bought nothing that `?? Math.random` does not already do. Teardown owns its own log. A two-member reason union crossed a module boundary just to reach a console.warn, and the client re-derived `cell=` from relayOrigin when liveness already held `cellUrl`. Liveness now tears itself down and calls `terminate`; the client lost the import, the method, and the exported type. One probe object, one interval. `probeTimer` + `missedProbes` are now `probe: { timer, misses } | null`, so "no timer implies no misses" is structural instead of maintained by resetting in two places, and the sendProbe/onProbeUnanswered mutual recursion is a plain setInterval. Jitter is computed once per run rather than per tick -- one offset already desynchronizes the cohort. Honest probe label. If ping() throws, the old arm path returned false and the caller logged `probe=in-flight/0` moments after terminating the socket -- a false statement in the line that exists for incident forensics. The arm path now returns the label it means, including `probe=send-failed`. Absorbed RelayControlSilenceWatchdog. It had one consumer and no test file, and this branch had to punch a `lastInboundTime` getter through it purely so liveness could read state it holds. `lastInboundAt` now sits next to `openedAt`; the file, the getter, the import, and the onDead('silence-limit') lambda are all gone. Also: dropped `RelayControlRequestTimeout.reqId` and `PendingRequest.sentAt` (both written, never read -- the timeout closure captures the local `sentAt`); dropped the two `'n/a'` branches, unreachable because a request timeout can only fire after sendActive succeeded, which requires a state only handleProofMessage reaches on the line before it calls liveness.start(); moved the classifier invariant off a void-returning callback type and onto REQUEST_TIMEOUT_CODE, where an edit to the string is next to the warning about editing the string; and replaced the `live` parameter with an `isLive()` option so liveness asks rather than being told, which also let `liveness` be constructed before `requests` instead of a closure reading a field assigned on a later line. --- .../relay/relay-control-client-options.ts | 3 +- .../relay/relay-control-client.test.ts | 162 ++++++++++++++++- .../runtime/relay/relay-control-client.ts | 29 ++-- .../runtime/relay/relay-control-liveness.ts | 163 ++++++++++++++++++ .../runtime/relay/relay-control-requests.ts | 24 ++- .../relay/relay-control-silence-watchdog.ts | 37 ---- ...lay-control-timeout-classification.test.ts | 34 ++++ src/shared/mobile-relay-mint-failure.test.ts | 2 + 8 files changed, 400 insertions(+), 54 deletions(-) create mode 100644 src/main/runtime/relay/relay-control-liveness.ts delete mode 100644 src/main/runtime/relay/relay-control-silence-watchdog.ts create mode 100644 src/main/runtime/relay/relay-control-timeout-classification.test.ts diff --git a/src/main/runtime/relay/relay-control-client-options.ts b/src/main/runtime/relay/relay-control-client-options.ts index 93d1efc0fc6..a5815852242 100644 --- a/src/main/runtime/relay/relay-control-client-options.ts +++ b/src/main/runtime/relay/relay-control-client-options.ts @@ -18,5 +18,6 @@ export type RelayControlClientOptions = { onPendingChanged?: () => void createSocket?: (url: string, relayJwt: string) => WebSocket connectDeadlineMs?: number - silenceLimitMs?: number + // Test seam: deterministic probe jitter. + livenessRandom?: () => number } diff --git a/src/main/runtime/relay/relay-control-client.test.ts b/src/main/runtime/relay/relay-control-client.test.ts index 6896b1064e7..1360e8c4e66 100644 --- a/src/main/runtime/relay/relay-control-client.test.ts +++ b/src/main/runtime/relay/relay-control-client.test.ts @@ -460,12 +460,32 @@ class FakeControlSocket extends EventEmitter { this.close(1006) } + pings = 0 + + ping(): void { + if (this.readyState !== 1) { + throw new Error('socket_not_open') + } + this.pings += 1 + } + + /** The RFC 6455 reply a live peer owes any ping, delivered out of band. */ + pong(): void { + this.emit('pong') + } + deliver(message: ControlFrame): void { this.emit('message', JSON.stringify(message), false) } } -function scriptedControl(options: { closeWithAck?: boolean; issuedAtOffsetMs?: number } = {}): { +function scriptedControl( + options: { + closeWithAck?: boolean + issuedAtOffsetMs?: number + livenessRandom?: () => number + } = {} +): { client: RelayControlClient socket: FakeControlSocket onConnectionOpen: ReturnType @@ -539,6 +559,8 @@ function scriptedControl(options: { closeWithAck?: boolean; issuedAtOffsetMs?: n const onConnectionOpen = vi.fn() const client = new RelayControlClient({ cellUrl: origin, + // Midpoint random => no jitter, so probe boundaries are exact in tests. + livenessRandom: options.livenessRandom ?? (() => 0.5), relayJwt: 'scoped-token', relayHostId, assignmentEpoch: 3, @@ -678,3 +700,141 @@ describe('RelayControlClient scripted-socket lifecycle', () => { expect(onClose).toHaveBeenCalledWith(MOBILE_RELAY_CLOSE_CODE.BAD_OUTER_CREDENTIAL) }) }) + +// STA-7672: a Windows desktop behind NAT/VPN (or resuming from sleep) can hold a +// half-open control socket that send() writes into happily while nothing comes +// back. Every pairing request then failed at its 10s deadline against a socket +// the 75s silence watchdog would not reap for another minute-plus. +describe('RelayControlClient half-open recovery', () => { + afterEach(() => { + vi.useRealTimers() + }) + + it('tears down only after a run of unanswered probes, not the first one', async () => { + vi.useFakeTimers() + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) + const { client, socket, onClose } = scriptedControl() + await client.connect() + const invite = client.createInvite('device-1').catch((error: Error) => error.message) + + await vi.advanceTimersByTimeAsync(10_000) + + // The request deadline alone must not close the control — a close would have + // rejected as relay_control_closed_ instead. + expect(await invite).toBe('relay_control_request_timeout') + expect(socket.pings).toBe(1) + expect(socket.readyState).toBe(1) + + // One unanswered probe is UNKNOWN, not death (STA-3320): a lone swallowed + // pong is routine on exactly the VPN/cellular paths this detection targets. + await vi.advanceTimersByTimeAsync(8_000) + expect(socket.pings).toBe(2) + expect(socket.readyState).toBe(1) + await vi.advanceTimersByTimeAsync(8_000) + expect(socket.pings).toBe(3) + expect(socket.readyState).toBe(1) + + // Third consecutive miss is evidence. + await vi.advanceTimersByTimeAsync(8_000) + expect(socket.readyState).toBe(3) + expect(onClose).toHaveBeenCalledWith(1006) + expect(client.isLive()).toBe(false) + // Named in the log so a fleet-wide false positive would be visible. + expect(warn).toHaveBeenCalledWith(expect.stringContaining('reason=probe-unanswered')) + warn.mockRestore() + }) + + it('retires the whole probe run on a single pong', async () => { + vi.useFakeTimers() + const { client, socket, onClose } = scriptedControl() + await client.connect() + const invite = client.createInvite('device-1').catch((error: Error) => error.message) + + await vi.advanceTimersByTimeAsync(10_000) + await invite + await vi.advanceTimersByTimeAsync(8_000) + expect(socket.pings).toBe(2) + socket.pong() + + // A later probe run must start from zero, not inherit the earlier miss. + await vi.advanceTimersByTimeAsync(40_000) + expect(socket.readyState).toBe(1) + expect(onClose).not.toHaveBeenCalled() + expect(client.isLive()).toBe(true) + }) + + it("clears an armed probe on the relay's next ping, with no pong involved", async () => { + vi.useFakeTimers() + const { client, socket, onClose } = scriptedControl() + await client.connect() + const invite = client.createInvite('device-1').catch((error: Error) => error.message) + + await vi.advanceTimersByTimeAsync(10_000) + expect(socket.pings).toBe(1) + await invite + + // The probe run (3 x 8s) outlasts the relay's 15s ping cadence on purpose: + // relay liveness runs at the application layer, so a middlebox that swallows + // RFC 6455 control frames must not be able to make this a reconnect loop. + await vi.advanceTimersByTimeAsync(15_000) + socket.deliver({ type: 'ping', t: Date.now() }) + await vi.advanceTimersByTimeAsync(40_000) + + expect(socket.readyState).toBe(1) + expect(onClose).not.toHaveBeenCalled() + expect(client.isLive()).toBe(true) + }) + + it('does not probe a control that kept talking while a request went unanswered', async () => { + vi.useFakeTimers() + const { client, socket } = scriptedControl() + await client.connect() + const invite = client.createInvite('device-1').catch((error: Error) => error.message) + + await vi.advanceTimersByTimeAsync(5_000) + socket.deliver({ type: 'ping', t: Date.now() }) + await vi.advanceTimersByTimeAsync(5_000) + + // A reply running past its deadline under relay DB load is not a dead + // socket; tearing this control down would strand every phone on the cell. + expect(await invite).toBe('relay_control_request_timeout') + expect(socket.pings).toBe(0) + expect(client.isLive()).toBe(true) + }) + + it('spreads probe deadlines so one slow cell cannot synchronize a cohort', async () => { + vi.useFakeTimers() + // Earliest jitter (-10%) fires at 7.2s; the unjittered boundary is 8s. + const { client, socket } = scriptedControl({ livenessRandom: () => 0 }) + await client.connect() + void client.createInvite('device-1').catch(() => undefined) + + await vi.advanceTimersByTimeAsync(10_000) + expect(socket.pings).toBe(1) + await vi.advanceTimersByTimeAsync(7_300) + expect(socket.pings).toBe(2) + }) + + it('logs the cell and the silence without altering the rejection', async () => { + vi.useFakeTimers() + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) + const { client } = scriptedControl() + await client.connect() + const invite = client.createInvite('device-1').catch((error: Error) => error.message) + + await vi.advanceTimersByTimeAsync(10_000) + + // The message is a classification key: mobile-relay-mint-failure.ts matches + // it against an anchored /^relay_[a-z0-9_]{1,74}$/, so a diagnostic suffix + // silently downgrades this to the generic relay_mint_failed fallback. + expect(await invite).toBe('relay_control_request_timeout') + + const logged = warn.mock.calls.map((call) => String(call[0])).join('\n') + expect(logged).toContain('reqKind=invite') + expect(logged).toContain('cell=http://relay.test') + expect(logged).toContain('socketAgeMs=10000') + expect(logged).toContain('sinceInboundMs=10000') + expect(logged).toContain('probe=armed') + warn.mockRestore() + }) +}) diff --git a/src/main/runtime/relay/relay-control-client.ts b/src/main/runtime/relay/relay-control-client.ts index e60c4a58de8..ea2632d11e5 100644 --- a/src/main/runtime/relay/relay-control-client.ts +++ b/src/main/runtime/relay/relay-control-client.ts @@ -18,10 +18,7 @@ import { import { RelayControlRequests } from './relay-control-requests' import type { DeviceCredentialInstallAuthorization } from './relay-control-requests' import { answerRelayHostChallenge } from './relay-host-proof' -import { - RELAY_CONTROL_SILENCE_LIMIT_MS, - RelayControlSilenceWatchdog -} from './relay-control-silence-watchdog' +import { RelayControlLiveness } from './relay-control-liveness' import { closeRelayControlSocket } from './relay-control-socket-close' import { controlWebSocketUrl } from './relay-control-url' @@ -34,23 +31,28 @@ export class RelayControlClient { private readonly relayOrigin: string private readonly controlUrl: string private readonly createSocket: NonNullable + private readonly liveness: RelayControlLiveness private readonly requests: RelayControlRequests private socket: WebSocket | null = null private state: RelayControlState = 'idle' private connectResolve: ((ack: RelayHostHelloAckMessage) => void) | null = null private connectReject: ((error: Error) => void) | null = null private connectTimer: ReturnType | null = null - private readonly silenceWatchdog: RelayControlSilenceWatchdog constructor(options: RelayControlClientOptions) { this.options = options - this.requests = new RelayControlRequests(options.onPendingChanged) const endpoint = controlWebSocketUrl(options.cellUrl) this.relayOrigin = endpoint.origin this.controlUrl = endpoint.url - this.silenceWatchdog = new RelayControlSilenceWatchdog( - options.silenceLimitMs ?? RELAY_CONTROL_SILENCE_LIMIT_MS, - () => this.socket?.terminate() + this.liveness = new RelayControlLiveness({ + cellUrl: this.relayOrigin, + ping: () => this.socket?.ping(), + isLive: () => this.isLive(), + terminate: () => this.socket?.terminate(), + random: options.livenessRandom + }) + this.requests = new RelayControlRequests(options.onPendingChanged, (timeout) => + this.liveness.noteRequestTimeout(timeout) ) this.createSocket = options.createSocket ?? @@ -70,8 +72,9 @@ export class RelayControlClient { const socket = this.createSocket(this.controlUrl, this.options.relayJwt) this.socket = socket socket.once('open', () => this.sendHostHello()) + socket.on('pong', () => this.liveness.notePong()) socket.on('message', (raw, isBinary) => { - this.silenceWatchdog.noteInbound() + this.liveness.noteInbound() if (isBinary) { this.failProtocol('binary control message') return @@ -157,7 +160,7 @@ export class RelayControlClient { closeNow(hostCloseReason?: RelayHostCloseReason): void { const wasConnecting = this.state === 'opening' || this.state === 'proving' this.state = 'closed' - this.silenceWatchdog.stop() + this.liveness.stop() if (wasConnecting) { this.connectReject?.(new Error('relay_control_closed')) this.clearConnectPromise() @@ -267,7 +270,7 @@ export class RelayControlClient { return } this.state = 'active' - this.silenceWatchdog.start() + this.liveness.start() this.connectResolve?.(ack.data) this.clearConnectPromise() } @@ -288,7 +291,7 @@ export class RelayControlClient { private handleClose(code: number): void { const wasConnecting = this.state === 'opening' || this.state === 'proving' this.state = 'closed' - this.silenceWatchdog.stop() + this.liveness.stop() if (wasConnecting) { this.connectReject?.(new Error(`relay_control_closed_${code}`)) this.clearConnectPromise() diff --git a/src/main/runtime/relay/relay-control-liveness.ts b/src/main/runtime/relay/relay-control-liveness.ts new file mode 100644 index 00000000000..0a6bef02d60 --- /dev/null +++ b/src/main/runtime/relay/relay-control-liveness.ts @@ -0,0 +1,163 @@ +import type { RelayControlRequestTimeout } from './relay-control-requests' +import { RELAY_RENEWAL_JITTER_RATIO } from './relay-renewal-jitter' + +// STA-7672: a control request that times out has two indistinguishable causes — +// a loaded relay whose reply is late, or a half-open TCP socket that swallowed +// the send (common on Windows behind NAT/VPN or across sleep-resume, where the +// OS reports the write as succeeding). A close would have rejected as +// `relay_control_closed_`, so a timeout proves the socket stayed open and +// never answered. An RFC 6455 ping settles which cause it was without spending +// an application opcode: any live peer must answer with a pong. + +// The relay pings every 15s and closes a control after 75s of silence; mirror +// that bound so a dead or server-side-unindexed socket cannot stay "active". +const SILENCE_LIMIT_MS = 75_000 +const SILENCE_CHECK_INTERVAL_MS = 15_000 + +// Three of these resolve a suspect socket in 24s, well inside the silence bound +// above — which on Windows let a user burn every pairing attempt before it fired. +const PROBE_INTERVAL_MS = 8_000 + +// One unanswered probe is UNKNOWN, not death: a cellular/VPN blackhole or a +// stalled TCP retransmit routinely swallows a lone pong (STA-3320). The run this +// requires also makes the window (24s) outlast the relay's own 15s ping, so a +// middlebox that swallows every pong still cannot force a reconnect loop — the +// cell's ping lands inside the window and clears the run. A teardown therefore +// means the pipe carried neither frame, three times over. +const PROBE_MISS_LIMIT = 3 + +type TeardownReason = 'probe-unanswered' | 'silence-limit' + +export type RelayControlLivenessOptions = { + cellUrl: string + ping: () => void + isLive: () => boolean + terminate: () => void + random?: () => number +} + +/** Everything that decides whether a control socket is still reachable. */ +export class RelayControlLiveness { + private readonly random: () => number + private probe: { timer: ReturnType; misses: number } | null = null + private silenceTimer: ReturnType | null = null + private openedAt = 0 + private lastInboundAt = 0 + + constructor(private readonly options: RelayControlLivenessOptions) { + this.random = options.random ?? Math.random + } + + start(): void { + this.openedAt = Date.now() + this.lastInboundAt = this.openedAt + this.silenceTimer = setInterval(() => { + if (Date.now() - this.lastInboundAt > SILENCE_LIMIT_MS) { + this.tearDown('silence-limit') + } + }, SILENCE_CHECK_INTERVAL_MS) + this.silenceTimer.unref?.() + } + + noteInbound(): void { + this.lastInboundAt = Date.now() + this.clearProbe() + } + + // A pong proves the pipe and nothing more — it can come from a socket the + // relay has already unindexed — so it clears a probe but never advances + // `lastInboundAt`, whose job is to mirror the relay's own 75s bound. + notePong(): void { + this.clearProbe() + } + + stop(): void { + if (this.silenceTimer) { + clearInterval(this.silenceTimer) + this.silenceTimer = null + } + this.clearProbe() + } + + /** + * Probe only when nothing at all arrived since the send: then the relay's own + * ping is overdue too, which is the half-open signature rather than a reply + * running late under load. Otherwise this just records why the request failed. + */ + noteRequestTimeout(timeout: RelayControlRequestTimeout): void { + const now = Date.now() + const diagnostics = [ + `reqKind=${timeout.kind}`, + `cell=${this.options.cellUrl}`, + `socketAgeMs=${now - this.openedAt}`, + `sinceInboundMs=${now - this.lastInboundAt}` + ] + if (this.options.isLive() && this.lastInboundAt <= timeout.sentAt) { + diagnostics.push(this.armProbe()) + } + // Logged rather than appended to the rejection, which is a classification + // key; the pairing flow discards the rejection's text entirely. + console.warn(`[relay] control request timed out ${diagnostics.join(' ')}`) + } + + /** Starts a probe run if none is live; returns what to report in the log. */ + private armProbe(): string { + if (this.probe) { + return `probe=in-flight/${this.probe.misses}` + } + if (!this.sendProbe()) { + return 'probe=send-failed' + } + // One jitter offset per run is enough to keep a cohort timing out against + // the same slow cell off a shared boundary (see RELAY_RENEWAL_JITTER_RATIO). + const spread = (this.random() * 2 - 1) * RELAY_RENEWAL_JITTER_RATIO + const timer = setInterval( + () => this.onProbeMissed(), + Math.max(1, Math.floor(PROBE_INTERVAL_MS * (1 + spread))) + ) + timer.unref?.() + this.probe = { timer, misses: 0 } + return 'probe=armed' + } + + private onProbeMissed(): void { + const probe = this.probe + if (!probe) { + return + } + probe.misses += 1 + if (probe.misses >= PROBE_MISS_LIMIT) { + this.tearDown('probe-unanswered') + return + } + this.sendProbe() + } + + private sendProbe(): boolean { + try { + this.options.ping() + return true + } catch { + // A ping that throws on a live control is already the answer. + this.tearDown('probe-unanswered') + return false + } + } + + /** Any inbound frame — pong or application message — retires the probe run. */ + private clearProbe(): void { + if (this.probe) { + clearInterval(this.probe.timer) + this.probe = null + } + } + + // A teardown lands on the origin as an ordinary 1006 close, so name the cause + // here: without it a probe-driven reconnect is indistinguishable from any + // other drop, and a fleet-wide false positive would be invisible. + private tearDown(reason: TeardownReason): void { + this.stop() + console.warn(`[relay] control torn down cell=${this.options.cellUrl} reason=${reason}`) + this.options.terminate() + } +} diff --git a/src/main/runtime/relay/relay-control-requests.ts b/src/main/runtime/relay/relay-control-requests.ts index 6151d634f0d..eeba886be23 100644 --- a/src/main/runtime/relay/relay-control-requests.ts +++ b/src/main/runtime/relay/relay-control-requests.ts @@ -18,6 +18,20 @@ type PendingRequest = { timer: ReturnType } +export type RelayControlRequestTimeout = { + kind: PendingRequest['kind'] + sentAt: number +} + +/** Notified when a request hits its deadline, so liveness can probe the socket. */ +export type OnRelayControlRequestTimeout = (timeout: RelayControlRequestTimeout) => void + +// A classification key, not prose: consumers exact-match this against +// /^relay_[a-z0-9_]{1,74}$/ (src/shared/mobile-relay-mint-failure.ts), so any +// appended diagnostic downgrades a precise code to the generic fallback. +// Diagnostics belong in the log — see RelayControlLiveness.noteRequestTimeout. +const REQUEST_TIMEOUT_CODE = 'relay_control_request_timeout' + export type DeviceCredentialInstallAuthorization = | { mode: 'relay-basis'; basisConnId: string } | { mode: 'authenticated-direct'; directAuthId: string } @@ -42,7 +56,10 @@ type SendRelayControlRequest = (payload: RelayControlRequestPayload) => void export class RelayControlRequests { private readonly pending = new Map() - constructor(private readonly onPendingChanged?: () => void) {} + constructor( + private readonly onPendingChanged?: () => void, + private readonly onTimeout?: OnRelayControlRequestTimeout + ) {} get size(): number { return this.pending.size @@ -170,10 +187,13 @@ export class RelayControlRequests { if (this.pending.has(reqId)) { return Promise.reject(new Error('duplicate_relay_request_id')) } + const sentAt = Date.now() return new Promise((resolve, reject) => { const timer = setTimeout(() => { this.finish(reqId) - reject(new Error('relay_control_request_timeout')) + // Runs before the reject so the probe observes the socket as the deadline found it. + this.onTimeout?.({ kind, sentAt }) + reject(new Error(REQUEST_TIMEOUT_CODE)) }, 10_000) this.pending.set(reqId, { kind, resolve, reject, timer }) try { diff --git a/src/main/runtime/relay/relay-control-silence-watchdog.ts b/src/main/runtime/relay/relay-control-silence-watchdog.ts deleted file mode 100644 index 4015dea3dd8..00000000000 --- a/src/main/runtime/relay/relay-control-silence-watchdog.ts +++ /dev/null @@ -1,37 +0,0 @@ -// The relay pings every 15s and closes a control after 75s of silence; mirror -// that bound so a dead or server-side-unindexed socket cannot stay "active". -export const RELAY_CONTROL_SILENCE_LIMIT_MS = 75_000 - -const CHECK_INTERVAL_MS = 15_000 - -export class RelayControlSilenceWatchdog { - private timer: ReturnType | null = null - private lastInboundAt = 0 - - constructor( - private readonly limitMs: number, - private readonly onSilence: () => void - ) {} - - noteInbound(): void { - this.lastInboundAt = Date.now() - } - - start(): void { - this.lastInboundAt = Date.now() - this.timer = setInterval(() => { - if (Date.now() - this.lastInboundAt > this.limitMs) { - this.stop() - this.onSilence() - } - }, CHECK_INTERVAL_MS) - this.timer.unref?.() - } - - stop(): void { - if (this.timer) { - clearInterval(this.timer) - this.timer = null - } - } -} diff --git a/src/main/runtime/relay/relay-control-timeout-classification.test.ts b/src/main/runtime/relay/relay-control-timeout-classification.test.ts new file mode 100644 index 00000000000..ca8f8c1d79e --- /dev/null +++ b/src/main/runtime/relay/relay-control-timeout-classification.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it, vi } from 'vitest' +import { mobileRelayMintFailureFromUnknown } from '../../../shared/mobile-relay-mint-failure' +import { RelayControlRequests } from './relay-control-requests' + +// A control-request rejection is a classification key, not prose: the pairing +// flow feeds error.message through an anchored /^relay_[a-z0-9_]{1,74}$/ and +// falls back to a generic code on any mismatch. Appending diagnostics to that +// message once turned every pairing timeout into relay_mint_failed, losing the +// one signal that identified STA-7672 — so pin the contract end to end. +describe('control request timeout classification', () => { + it('keeps a timed-out request classifiable by the mobile pairing flow', async () => { + vi.useFakeTimers() + try { + const onTimeout = vi.fn() + const requests = new RelayControlRequests(undefined, onTimeout) + const settled = requests.createInvite('req-1', 'device-1', () => {}).catch((e: Error) => e) + + await vi.advanceTimersByTimeAsync(10_000) + const error = await settled + + expect(onTimeout).toHaveBeenCalledOnce() + expect( + mobileRelayMintFailureFromUnknown({ + error, + stage: 'create_pairing_relay', + fallbackCode: 'relay_mint_failed', + fallbackMessage: 'could not mint' + }).code + ).toBe('relay_control_request_timeout') + } finally { + vi.useRealTimers() + } + }) +}) diff --git a/src/shared/mobile-relay-mint-failure.test.ts b/src/shared/mobile-relay-mint-failure.test.ts index d0d8a768a8b..6a1b47ce8e8 100644 --- a/src/shared/mobile-relay-mint-failure.test.ts +++ b/src/shared/mobile-relay-mint-failure.test.ts @@ -1,6 +1,8 @@ import { describe, expect, it } from 'vitest' import { mobileRelayMintFailureFromUnknown } from './mobile-relay-mint-failure' +// The producer end of this contract is pinned separately, in +// src/main/runtime/relay/relay-control-timeout-classification.test.ts. describe('mobileRelayMintFailureFromUnknown', () => { it('keeps known machine-readable Relay codes for diagnostics', () => { expect( From d3032da299b493abcc1d9eaff5020a90f750d65f Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:11:18 -0700 Subject: [PATCH 044/168] fix(renderer): cancel signout auth retry on unmount (#20905) Co-authored-by: m4air --- src/renderer/src/components/UnexpectedSignoutCard.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/components/UnexpectedSignoutCard.tsx b/src/renderer/src/components/UnexpectedSignoutCard.tsx index dbfb106eb23..40035a8da92 100644 --- a/src/renderer/src/components/UnexpectedSignoutCard.tsx +++ b/src/renderer/src/components/UnexpectedSignoutCard.tsx @@ -59,6 +59,7 @@ export function UnexpectedSignoutCard(): React.JSX.Element | null { useEffect(() => { let cancelled = false let attempts = 0 + let retryTimer: number | null = null const refresh = (): void => { attempts += 1 void useAppStore @@ -71,13 +72,19 @@ export function UnexpectedSignoutCard(): React.JSX.Element | null { if (status != null) { setAuthRefreshReady(true) } else if (attempts < 3) { - window.setTimeout(refresh, 500) + retryTimer = window.setTimeout(() => { + retryTimer = null + refresh() + }, 500) } }) } refresh() return () => { cancelled = true + if (retryTimer !== null) { + window.clearTimeout(retryTimer) + } } }, []) From d7d3bcfc6653592be78ef5c6ea3298153eea4209 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:11:21 -0700 Subject: [PATCH 045/168] fix(renderer): cancel copied prompt reset on unmount (#20906) * fix(renderer): cancel copied prompt reset on unmount * fix: address memory PR review regressions and withdraw false positives --------- Co-authored-by: m4air Co-authored-by: m4air --- .../settings/EphemeralVmsPane.test.tsx | 45 +++++++++++++++++++ .../components/settings/EphemeralVmsPane.tsx | 20 ++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/components/settings/EphemeralVmsPane.test.tsx b/src/renderer/src/components/settings/EphemeralVmsPane.test.tsx index 8a68688f624..39400a827d8 100644 --- a/src/renderer/src/components/settings/EphemeralVmsPane.test.tsx +++ b/src/renderer/src/components/settings/EphemeralVmsPane.test.tsx @@ -138,6 +138,51 @@ describe('EphemeralVmsPane', () => { }) }) + it('does not schedule a reset when clipboard completion arrives after unmount', async () => { + let finishClipboard!: () => void + vi.mocked(window.api.ui.writeClipboardText).mockReturnValueOnce( + new Promise((resolve) => { + finishClipboard = resolve + }) + ) + const container = await renderPane() + const setTimeout = vi.spyOn(window, 'setTimeout') + try { + await act(async () => { + container.querySelector('button[aria-label="Copy"]')?.click() + }) + await act(async () => roots.pop()?.unmount()) + setTimeout.mockClear() + await act(async () => { + finishClipboard() + await Promise.resolve() + }) + expect(setTimeout.mock.calls.filter(([, delay]) => delay === 1500)).toHaveLength(0) + } finally { + setTimeout.mockRestore() + } + }) + + it('shows copied feedback while mounted and releases its reset on unmount', async () => { + const container = await renderPane() + const setTimeout = vi.spyOn(window, 'setTimeout') + const clearTimeout = vi.spyOn(window, 'clearTimeout') + try { + await act(async () => { + container.querySelector('button[aria-label="Copy"]')?.click() + }) + expect(container.querySelector('button[aria-label="Copy"]')?.textContent).toBe('Copied') + const timerIndex = setTimeout.mock.calls.findIndex(([, delay]) => delay === 1500) + expect(timerIndex).toBeGreaterThanOrEqual(0) + const timer = setTimeout.mock.results[timerIndex].value + await act(async () => roots.pop()?.unmount()) + expect(clearTimeout).toHaveBeenCalledWith(timer) + } finally { + setTimeout.mockRestore() + clearTimeout.mockRestore() + } + }) + it('refreshes the catalog when plugin content changes', async () => { const listRecipeCatalog = window.api.ephemeralVm.listRecipeCatalog as ReturnType const container = await renderPane() diff --git a/src/renderer/src/components/settings/EphemeralVmsPane.tsx b/src/renderer/src/components/settings/EphemeralVmsPane.tsx index acd042444c3..7b0086ae78b 100644 --- a/src/renderer/src/components/settings/EphemeralVmsPane.tsx +++ b/src/renderer/src/components/settings/EphemeralVmsPane.tsx @@ -45,6 +45,15 @@ export function EphemeralVmsPane(): React.JSX.Element { const [promptCopied, setPromptCopied] = useState(false) const mountedRef = useMountedRef() const refreshGenerationRef = useRef(0) + const promptResetTimerRef = useRef(null) + + useEffect(() => { + return () => { + if (promptResetTimerRef.current !== null) { + window.clearTimeout(promptResetTimerRef.current) + } + } + }, []) // Why: an absent runtime still resolves to the local host, which is what the // seven sibling panes rely on to reach the Windows npx preflight. @@ -126,8 +135,17 @@ export function EphemeralVmsPane(): React.JSX.Element { try { await window.api.ui.writeClipboardText(AGENT_PROMPT) useAppStore.getState().recordFeatureInteraction('ephemeral-vm-setup') + if (!mountedRef.current) { + return + } setPromptCopied(true) - setTimeout(() => setPromptCopied(false), 1500) + if (promptResetTimerRef.current !== null) { + window.clearTimeout(promptResetTimerRef.current) + } + promptResetTimerRef.current = window.setTimeout(() => { + promptResetTimerRef.current = null + setPromptCopied(false) + }, 1500) } catch { toast.error( translate( From bd404185f19d077065f926c06574c039a7986392 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:11:49 -0700 Subject: [PATCH 046/168] fix(renderer): release parked terminal scroll intents (#20924) * fix: release scroll intents for closed parked tabs * fix(renderer): release scroll intents on worktree removal * test(renderer): cover parked worktree intent cleanup --------- Co-authored-by: m4air --- ...inal-parked-watcher-reconciliation.test.ts | 39 +++++++++++++++++++ .../terminal-parked-watcher-registry.ts | 17 +++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts b/src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts index f59d8b4500e..f92d34c00a6 100644 --- a/src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts +++ b/src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts @@ -1,12 +1,17 @@ import { afterEach, describe, expect, it } from 'vitest' import { captureParkedTerminalPaneCandidates, + pruneParkedTerminalWatchers, retireParkedTerminalTab } from './terminal-parked-watcher-registry' import { reconcileParkedWatcherPtyIds, resolveParkedTerminalPaneCandidates } from './terminal-parked-watcher-reconciliation' +import { + readTerminalScrollIntentKeyRetention, + writeKeyedTerminalScrollIntent +} from '../../lib/pane-manager/terminal-scroll-intent-key-store' const TAB_ID = 'tab-1' const WORKTREE_ID = 'repo::/worktree' @@ -86,6 +91,40 @@ describe('paired parked-watcher reconciliation', () => { }) }) +it('releases captured scroll-intent keys when a parked tab is closed', () => { + writeKeyedTerminalScrollIntent(FIRST_LEAF_ID, { + kind: 'pinnedViewport', + bufferType: 'normal', + viewportY: 4, + baseY: 12, + revision: 1 + }) + captureParkedTerminalPaneCandidates(TAB_ID, WORKTREE_ID, [ + { ptyId: FIRST_PTY_ID, paneId: 1, leafId: FIRST_LEAF_ID, drivesTabTitle: true } + ]) + + expect(readTerminalScrollIntentKeyRetention().intents).toBe(1) + retireParkedTerminalTab(TAB_ID) + expect(readTerminalScrollIntentKeyRetention().intents).toBe(0) +}) + +it('releases captured scroll-intent keys when a parked worktree is removed', () => { + writeKeyedTerminalScrollIntent(SECOND_LEAF_ID, { + kind: 'pinnedViewport', + bufferType: 'normal', + viewportY: 8, + baseY: 16, + revision: 1 + }) + captureParkedTerminalPaneCandidates(TAB_ID, WORKTREE_ID, [ + { ptyId: FIRST_PTY_ID, paneId: 1, leafId: SECOND_LEAF_ID, drivesTabTitle: true } + ]) + + pruneParkedTerminalWatchers(new Set()) + + expect(readTerminalScrollIntentKeyRetention().intents).toBe(0) +}) + // Why: the sole-newborn parity flag is a fact about the captured PTY, so the // layout-fallback rescue must carry it only while the leaf still binds that PTY. describe('untouchedFreshSpawn carry through the layout-fallback rescue', () => { diff --git a/src/renderer/src/components/terminal-pane/terminal-parked-watcher-registry.ts b/src/renderer/src/components/terminal-pane/terminal-parked-watcher-registry.ts index 7e305ac7fe3..020421e637c 100644 --- a/src/renderer/src/components/terminal-pane/terminal-parked-watcher-registry.ts +++ b/src/renderer/src/components/terminal-pane/terminal-parked-watcher-registry.ts @@ -10,6 +10,7 @@ import { discardPreHandlerPtyState, hasPreHandlerPtyExit } from './pty-pre-handler-buffer' import { parseRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id' import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants' +import { releaseTerminalScrollIntentKey } from '../../lib/pane-manager/terminal-scroll-intent-key-store' export type ParkedTerminalPaneCapture = { ptyId: string | null @@ -140,7 +141,16 @@ export function retireParkedTerminalTab(tabId: string): void { // Why: explicit tab retirement permanently invalidates both live parked // observers and unmounted-pane candidates; neither may reattach later. disposeParkedTabWatchers(tabId) - capturedPanesByTabId.delete(tabId) + const capture = capturedPanesByTabId.get(tabId) + if (capture) { + // Parked panes never run PaneManager's close teardown. Release their + // strong scroll-intent keys here or every closed parked tab leaks one per + // leaf for the renderer lifetime. + for (const pane of capture.panes) { + releaseTerminalScrollIntentKey(pane.leafId) + } + capturedPanesByTabId.delete(tabId) + } } /** @@ -215,6 +225,11 @@ export function pruneParkedTerminalWatchers(liveWorktreeIds: ReadonlySet } for (const [tabId, capture] of capturedPanesByTabId) { if (!liveWorktreeIds.has(capture.worktreeId)) { + for (const pane of capture.panes) { + // Worktree removal can bypass closeTab while panes are parked; release + // the same strong scroll-intent keys as explicit tab retirement. + releaseTerminalScrollIntentKey(pane.leafId) + } capturedPanesByTabId.delete(tabId) } } From df88f83c707487add7715ed00ee0b754d6c76796 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:11:52 -0700 Subject: [PATCH 047/168] fix(relay): bound descendant traversal on cyclic process snapshots (#20946) Co-authored-by: m4air --- .../pty-shell-utils-process-cycles.test.ts | 55 +++++++++++++++++++ src/relay/pty-shell-utils.ts | 6 ++ 2 files changed, 61 insertions(+) create mode 100644 src/relay/pty-shell-utils-process-cycles.test.ts diff --git a/src/relay/pty-shell-utils-process-cycles.test.ts b/src/relay/pty-shell-utils-process-cycles.test.ts new file mode 100644 index 00000000000..aa074651ced --- /dev/null +++ b/src/relay/pty-shell-utils-process-cycles.test.ts @@ -0,0 +1,55 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { getProcessTableIndex } from '../shared/process-table-index' +import type { ProcessTableRow } from '../shared/process-table-snapshot' +import { getProcessTableSnapshot } from '../shared/process-table-snapshot-reader' +import { getForegroundProcessName } from './pty-shell-utils' + +vi.mock(import('../shared/process-table-snapshot-reader'), async (importOriginal) => ({ + ...(await importOriginal()), + getProcessTableSnapshot: vi.fn() +})) + +function row(pid: number, ppid: number, command = 'bash'): ProcessTableRow { + return { pid, ppid, stat: 'S+', command } +} + +describe('relay foreground process snapshot cycles', () => { + let platform: PropertyDescriptor | undefined + + beforeEach(() => { + platform = Object.getOwnPropertyDescriptor(process, 'platform') + Object.defineProperty(process, 'platform', { configurable: true, value: 'darwin' }) + }) + + afterEach(() => { + vi.restoreAllMocks() + if (platform) { + Object.defineProperty(process, 'platform', platform) + } + }) + + it.each([ + ['a self-parented root', [row(100, 100), row(101, 100, 'node /usr/bin/codex')]], + ['a two-process cycle', [row(100, 101), row(101, 100, 'node /usr/bin/codex')]], + [ + 'duplicate rows', + [row(100, 1), row(101, 100, 'node /usr/bin/codex'), row(101, 100, 'node /usr/bin/codex')] + ], + ['an ordinary tree', [row(100, 1), row(101, 100, 'node /usr/bin/codex')]] + ])('resolves the agent once for %s', async (_name, rows) => { + vi.mocked(getProcessTableSnapshot).mockResolvedValue(rows) + const children = getProcessTableIndex(rows).childrenByPpid + const readChildren = children.get.bind(children) + let reads = 0 + vi.spyOn(children, 'get').mockImplementation((pid) => { + // Bound the regression itself so removing the guard cannot OOM the test worker. + if (++reads > 20) { + throw new Error('process snapshot traversal did not terminate') + } + return readChildren(pid) + }) + + await expect(getForegroundProcessName(100, 'node')).resolves.toBe('codex') + expect(reads).toBeLessThanOrEqual(rows.length) + }) +}) diff --git a/src/relay/pty-shell-utils.ts b/src/relay/pty-shell-utils.ts index 9c8585933a1..0deaf8c6dd6 100644 --- a/src/relay/pty-shell-utils.ts +++ b/src/relay/pty-shell-utils.ts @@ -184,9 +184,15 @@ function collectDescendants( rootPid: number ): (ProcessTableRow & { depth: number })[] { const descendants: (ProcessTableRow & { depth: number })[] = [] + const seen = new Set([rootPid]) const stack = (index.childrenByPpid.get(rootPid) ?? []).map((row) => ({ row, depth: 1 })) while (stack.length > 0) { const { row, depth } = stack.pop()! + // Process snapshots can contain duplicate PIDs or cycles during reparenting. + if (seen.has(row.pid)) { + continue + } + seen.add(row.pid) descendants.push({ ...row, depth }) for (const child of index.childrenByPpid.get(row.pid) ?? []) { stack.push({ row: child, depth: depth + 1 }) From e9c04fb8d95254f0cb29addf669d4a597f8ca9a7 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:11:55 -0700 Subject: [PATCH 048/168] fix(ai-vault): ignore cancellations after request settlement (#20980) Co-authored-by: m4air --- docs/audits/scanner-late-cancel/README.md | 51 +++++ docs/audits/scanner-late-cancel/reproduce.mjs | 94 +++++++++ docs/audits/scanner-late-cancel/results.json | 27 +++ ...ssion-scanner-service-cancellation.test.ts | 187 ++++++++++++++++++ .../ai-vault/session-scanner-service-entry.ts | 3 + 5 files changed, 362 insertions(+) create mode 100644 docs/audits/scanner-late-cancel/README.md create mode 100644 docs/audits/scanner-late-cancel/reproduce.mjs create mode 100644 docs/audits/scanner-late-cancel/results.json create mode 100644 src/main/ai-vault/session-scanner-service-cancellation.test.ts diff --git a/docs/audits/scanner-late-cancel/README.md b/docs/audits/scanner-late-cancel/README.md new file mode 100644 index 00000000000..1d4a9a7044b --- /dev/null +++ b/docs/audits/scanner-late-cancel/README.md @@ -0,0 +1,51 @@ +# AI Vault scanner late cancellation + +The scanner child kept cancellation IDs after their requests had already settled. +Its response can still be in transit when the parent sends a cancellation, so this +does not require an invalid caller. The completed request has already run its +cleanup; nothing remains to delete the newly inserted ID. + +The fix admits cancellation only while the existing `pending` set owns the request. +That set includes both queued and running requests. Their cancellation and cleanup +remain unchanged. No protocol or history-retention policy changes. + +## Proof + +Run from the repository root with dependencies installed: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --max-old-space-size=128 docs/audits/scanner-late-cancel/reproduce.mjs +``` + +The script loads the checked-out production entry and derives the before version by +removing only the three-line `pending` membership guard in memory. It checks that +the guard occurs exactly once; no historical commit or Git access is needed. +Esbuild strips TypeScript before both versions run in separate VM contexts. Only +imported collaborators are stubbed: the production message handler, request lanes, +sets, and cleanup run. After each synthetic first-prompt request completes, its +matching cancel arrives. + +The script asserts the counts and emits JSON with both source SHA-256 hashes and +Node/platform/heap-limit provenance. [results.json](./results.json) records a run on +Node v26.6.0 with a 128 MiB old-space limit. This measures retained entries, not RSS. + +| Source | Requests/responses | Pending | Controllers | Retained cancel IDs | +| ------ | -----------------: | ------: | ----------: | ------------------: | +| Before | 1,000 / 1,000 | 0 | 0 | 1,000 | +| After | 1,000 / 1,000 | 0 | 0 | 0 | + +The regression test imports the production entry and directly observes its existing +cancellation set through an admitted cancellation. It repeats late cancels after +both successful and failed requests, verifies queued/running cancellation, and +checks shutdown cleanup. No production diagnostics or test-only exports were added. + +```sh +ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/ai-vault/session-scanner-service-cancellation.test.ts src/main/ai-vault/session-scanner-service-entry.test.ts src/main/ai-vault/session-scanner-service-client.test.ts +``` + +## Incident scope + +The same unconditional insertion exists in release `v1.4.198`. This retains numeric +IDs in the scanner child, not transcript contents in Electron main. It is a concrete +small leak; it does not explain the reported roughly 26 MB/s main-process growth in +#19768 or establish the cause of #19831's scope-level peak memory measurements. diff --git a/docs/audits/scanner-late-cancel/reproduce.mjs b/docs/audits/scanner-late-cancel/reproduce.mjs new file mode 100644 index 00000000000..e0a0b56a2a3 --- /dev/null +++ b/docs/audits/scanner-late-cancel/reproduce.mjs @@ -0,0 +1,94 @@ +import assert from 'node:assert/strict' +import { createHash } from 'node:crypto' +import { EventEmitter } from 'node:events' +import { readFileSync } from 'node:fs' +import { fileURLToPath } from 'node:url' +import { getHeapStatistics } from 'node:v8' +import { runInNewContext } from 'node:vm' +import { transform } from 'esbuild' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1') +} + +const sourcePath = 'src/main/ai-vault/session-scanner-service-entry.ts' +const source = readFileSync(new URL(`../../../${sourcePath}`, import.meta.url), 'utf8') +const guard = ' if (!pending.has(raw.id)) {\n return\n }\n' +const requestCount = 1000 +assert.equal(source.split(guard).length, 2, 'Review changed baseline transform') + +async function run(version) { + const original = version === 'before' ? source.replace(guard, '') : source + const { code } = await transform(original, { loader: 'ts', format: 'esm', target: 'es2022' }) + const entry = code.replace(/^import[\s\S]*?from ['"][^'"]+['"];?\n/gm, '') + assert.equal(/^import\b/m.test(entry), false, 'Unexpected production import shape') + const processStub = new EventEmitter() + let responses = 0 + processStub.send = (message) => { + if (message.type === 'result') { + responses++ + } + } + processStub.pid = 1 + const context = { + process: processStub, + performance, + AbortController, + requestSessionSearchRoots: () => undefined, + SessionScannerServiceSearch: class { + handles() { + return false + } + }, + AI_VAULT_SERVICE_PROTOCOL_VERSION: 1, + aiVaultServiceLane: () => 'interactive', + isAiVaultServiceRequest: (raw) => raw.type === 'request', + readAiVaultFirstUserPrompt: async () => ({ prompt: null }), + inspect: undefined + } + runInNewContext( + `${entry}\ninspect = () => ({ pending: pending.size, controllers: controllers.size, cancelled: cancelled.size })`, + context, + { timeout: 1000, filename: fileURLToPath(new URL(`../../../${sourcePath}`, import.meta.url)) } + ) + try { + processStub.emit('message', { type: 'init', protocol: 1 }) + for (let id = 1; id <= requestCount; id++) { + processStub.emit('message', { + type: 'request', + id, + operation: 'firstPrompt', + request: { agent: 'claude', filePath: '/synthetic' } + }) + await new Promise(setImmediate) + processStub.emit('message', { type: 'cancel', id }) + } + const retained = context.inspect() + assert.equal(responses, requestCount) + assert.equal(retained.pending, 0) + assert.equal(retained.controllers, 0) + assert.equal(retained.cancelled, version === 'before' ? requestCount : 0) + return { + source: version === 'before' ? 'working tree without pending guard' : 'working tree', + sourceSha256: createHash('sha256').update(original).digest('hex'), + requests: requestCount, + responses, + ...retained + } + } finally { + processStub.removeAllListeners() + } +} + +const results = { + node: process.version, + platform: process.platform, + architecture: process.arch, + heapLimitBytes: getHeapStatistics().heap_size_limit, + sourcePath, + baselineTransform: 'Remove only the three-line pending-membership guard in memory', + harness: 'Production entry in isolated VM contexts; imported collaborators stubbed', + before: await run('before'), + after: await run('after') +} +process.stdout.write(`${JSON.stringify(results, null, 2)}\n`) diff --git a/docs/audits/scanner-late-cancel/results.json b/docs/audits/scanner-late-cancel/results.json new file mode 100644 index 00000000000..71df02a9ccd --- /dev/null +++ b/docs/audits/scanner-late-cancel/results.json @@ -0,0 +1,27 @@ +{ + "node": "v26.6.0", + "platform": "darwin", + "architecture": "arm64", + "heapLimitBytes": 234881024, + "sourcePath": "src/main/ai-vault/session-scanner-service-entry.ts", + "baselineTransform": "Remove only the three-line pending-membership guard in memory", + "harness": "Production entry in isolated VM contexts; imported collaborators stubbed", + "before": { + "source": "working tree without pending guard", + "sourceSha256": "846f9af9577dbaf14d05aa5a2eea9e16a44a28ef7993e6b04c43b65db1f1f44f", + "requests": 1000, + "responses": 1000, + "pending": 0, + "controllers": 0, + "cancelled": 1000 + }, + "after": { + "source": "working tree", + "sourceSha256": "e300d4922da94da09abb3c14bbb240ef9593d8a4834a37598c1c2f77b14dcbf5", + "requests": 1000, + "responses": 1000, + "pending": 0, + "controllers": 0, + "cancelled": 0 + } +} diff --git a/src/main/ai-vault/session-scanner-service-cancellation.test.ts b/src/main/ai-vault/session-scanner-service-cancellation.test.ts new file mode 100644 index 00000000000..8863006017e --- /dev/null +++ b/src/main/ai-vault/session-scanner-service-cancellation.test.ts @@ -0,0 +1,187 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { + AiVaultServiceChildMessage, + AiVaultServiceParentMessage +} from './session-scanner-service-protocol' +import { AI_VAULT_SERVICE_PROTOCOL_VERSION } from './session-scanner-service-protocol' + +const scanAiVaultSessions = vi.hoisted(() => vi.fn()) +const flushSessionParseCachePersist = vi.hoisted(() => vi.fn(async () => undefined)) +const closeSearch = vi.hoisted(() => vi.fn()) + +vi.mock('./session-scanner', () => ({ scanAiVaultSessions })) +vi.mock('./session-scanner-parse-cache', () => ({ invalidateSessionParseCacheEntry: vi.fn() })) +vi.mock('./session-parse-cache-persistence', () => ({ + flushSessionParseCachePersist, + initSessionParseCachePersistence: vi.fn() +})) +vi.mock('./session-scanner-service-search', () => ({ + SessionScannerServiceSearch: class { + handles(): boolean { + return false + } + close(): void { + closeSearch() + } + } +})) + +const result = { sessions: [], issues: [], scannedAt: '2026-09-15' } +const sent: AiVaultServiceChildMessage[] = [] +const disconnect = vi.fn() +let restoreProcess = (): void => undefined + +function emit(message: AiVaultServiceParentMessage): void { + process.emit('message', message) +} + +function scan(id: number): void { + emit({ type: 'request', id, operation: 'scan', options: {} }) +} + +function settle(): Promise { + return new Promise((resolve) => setImmediate(resolve)) +} + +function cancelAndObserveSet(id: number): Set { + const add = vi.spyOn(Set.prototype, 'add') + try { + emit({ type: 'cancel', id }) + const index = add.mock.calls.findIndex(([value]) => value === id) + const retained = add.mock.contexts[index] + if (!(retained instanceof Set)) { + throw new Error('Expected an admitted cancellation.') + } + return retained + } finally { + add.mockRestore() + } +} + +beforeEach(async () => { + vi.resetModules() + vi.clearAllMocks() + sent.length = 0 + scanAiVaultSessions.mockResolvedValue(result) + const sendDescriptor = Object.getOwnPropertyDescriptor(process, 'send') + const disconnectDescriptor = Object.getOwnPropertyDescriptor(process, 'disconnect') + const messageListeners = new Set(process.listeners('message')) + const disconnectListeners = new Set(process.listeners('disconnect')) + Object.defineProperty(process, 'send', { + configurable: true, + value: (message: AiVaultServiceChildMessage) => { + sent.push(message) + return true + } + }) + Object.defineProperty(process, 'disconnect', { configurable: true, value: disconnect }) + restoreProcess = () => { + for (const listener of process.listeners('message')) { + if (!messageListeners.has(listener)) { + process.removeListener('message', listener) + } + } + for (const listener of process.listeners('disconnect')) { + if (!disconnectListeners.has(listener)) { + process.removeListener('disconnect', listener) + } + } + if (sendDescriptor) { + Object.defineProperty(process, 'send', sendDescriptor) + } else { + Reflect.deleteProperty(process, 'send') + } + if (disconnectDescriptor) { + Object.defineProperty(process, 'disconnect', disconnectDescriptor) + } else { + Reflect.deleteProperty(process, 'disconnect') + } + } + await import('./session-scanner-service-entry') + emit({ + type: 'init', + protocol: AI_VAULT_SERVICE_PROTOCOL_VERSION, + sessionParseCache: null, + sessionSearch: null + }) +}) + +afterEach(async () => { + emit({ type: 'shutdown' }) + await settle() + restoreProcess() +}) + +describe('AI Vault service cancellation ownership', () => { + it.each(['result', 'error'] as const)( + 'does not retain late cancellations after a %s response', + async (responseType) => { + const first = Promise.withResolvers() + scanAiVaultSessions.mockReturnValueOnce(first.promise) + scan(1) + await settle() + const cancelled = cancelAndObserveSet(1) + first.resolve(result) + await settle() + expect(cancelled.size).toBe(0) + + if (responseType === 'error') { + scanAiVaultSessions.mockRejectedValue(new Error('Synthetic parse failure')) + } + for (let id = 2; id <= 65; id++) { + scan(id) + await settle() + expect(sent).toContainEqual(expect.objectContaining({ type: responseType, id })) + // The parent can cancel while this completed response is still in transit. + emit({ type: 'cancel', id }) + } + emit({ type: 'cancel', id: 999 }) + expect(cancelled.size).toBe(0) + } + ) + + it('cancels running and queued requests and releases both IDs when they settle', async () => { + const first = Promise.withResolvers() + const signals: AbortSignal[] = [] + scanAiVaultSessions.mockImplementation(({ signal }: { signal: AbortSignal }) => { + signals.push(signal) + return signals.length === 1 ? first.promise : Promise.resolve(result) + }) + scan(1) + scan(2) + await settle() + const cancelled = cancelAndObserveSet(1) + emit({ type: 'cancel', id: 2 }) + expect(signals).toHaveLength(1) + expect(signals[0]?.aborted).toBe(true) + expect(cancelled.size).toBe(2) + first.resolve(result) + await settle() + expect(signals).toHaveLength(2) + expect(signals[1]?.aborted).toBe(true) + expect(cancelled.size).toBe(0) + expect(sent.filter((message) => message.type === 'result')).toHaveLength(2) + }) + + it('aborts the running request and closes the service before ignoring later cancels', async () => { + const first = Promise.withResolvers() + let signal: AbortSignal | undefined + scanAiVaultSessions.mockImplementation((options: { signal: AbortSignal }) => { + signal = options.signal + return first.promise + }) + scan(1) + await settle() + const cancelled = cancelAndObserveSet(1) + emit({ type: 'shutdown' }) + expect(signal?.aborted).toBe(true) + first.resolve(result) + await settle() + expect(cancelled.size).toBe(0) + expect(closeSearch).toHaveBeenCalledOnce() + expect(flushSessionParseCachePersist).toHaveBeenCalledOnce() + expect(disconnect).toHaveBeenCalledOnce() + emit({ type: 'cancel', id: 1 }) + expect(cancelled.size).toBe(0) + }) +}) diff --git a/src/main/ai-vault/session-scanner-service-entry.ts b/src/main/ai-vault/session-scanner-service-entry.ts index 7458db74e3a..d1a9da14a61 100644 --- a/src/main/ai-vault/session-scanner-service-entry.ts +++ b/src/main/ai-vault/session-scanner-service-entry.ts @@ -187,6 +187,9 @@ process.on('message', (raw: AiVaultServiceParentMessage) => { return } if (raw?.type === 'cancel') { + if (!pending.has(raw.id)) { + return + } cancelled.add(raw.id) controllers.get(raw.id)?.abort() return From bdad0e0f00325e6242fb6240d6aaa3120d2bbaa0 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:11:58 -0700 Subject: [PATCH 049/168] fix(browser): release page callbacks when a guest is destroyed (#21010) * fix(browser): release page callbacks when a guest is destroyed * fix: address memory PR review regressions and withdraw false positives --------- Co-authored-by: m4air --- .../README.md | 40 +++ .../reproduce.mjs | 168 +++++++++ .../results.json | 321 ++++++++++++++++++ ...-manager-destroyed-guest-downloads.test.ts | 152 +++++++++ ...er-manager-destroyed-guest-test-fixture.ts | 69 ++++ .../browser-manager-destroyed-guest.test.ts | 148 ++++++++ .../browser-manager-download-lifecycle.ts | 31 ++ ...browser-manager-guest-navigation-policy.ts | 7 +- .../browser/browser-manager-registration.ts | 18 +- src/main/browser/browser-manager-state.ts | 5 +- 10 files changed, 953 insertions(+), 6 deletions(-) create mode 100644 docs/audits/browser-destroyed-guest-retention/README.md create mode 100644 docs/audits/browser-destroyed-guest-retention/reproduce.mjs create mode 100644 docs/audits/browser-destroyed-guest-retention/results.json create mode 100644 src/main/browser/browser-manager-destroyed-guest-downloads.test.ts create mode 100644 src/main/browser/browser-manager-destroyed-guest-test-fixture.ts create mode 100644 src/main/browser/browser-manager-destroyed-guest.test.ts diff --git a/docs/audits/browser-destroyed-guest-retention/README.md b/docs/audits/browser-destroyed-guest-retention/README.md new file mode 100644 index 00000000000..d07a30dc389 --- /dev/null +++ b/docs/audits/browser-destroyed-guest-retention/README.md @@ -0,0 +1,40 @@ +# Destroyed browser guests retain main-process callbacks + +An embedded browser guest's `destroyed` event called `cleanupGuestPolicyAttachment`. That removed its primary page-to-WebContents lookup but left four per-page cleanup callbacks that capture the destroyed WebContents wrapper, plus renderer/workspace/worktree/profile metadata. `unregisterAll` subsequently iterated only the now-empty primary lookup: three callback maps and the renderer/workspace metadata survived that cleanup too. + +Renderer reload can destroy guests without each page sending explicit unregister IPC. A later close of a restored, unmounted page does not send that IPC either: `destroyPersistentWebview` returns early when its renderer registry has no guest. Explicit unregister correctly releases these resources; same-page re-registration also replaces its callbacks. The defect affects destroyed owners that do not take either path. + +The fix routes destruction through the existing `unregisterGuest` with a guest-retirement reason only when that exact guest still owns the primary page ID. Already bound downloads retain their existing renderer routing until they settle; explicit page close still cancels them. Unregistered guests and popups retain policy-only cleanup. A stale callback cannot unregister a replacement. Normal renderer-process loss keeps its live WebContents and metadata for reload recovery; a fresh guest registration supplies its ownership metadata again. Shared browser sessions and sibling pages are untouched. + +## Download lifetime correction + +Review found that the initial fix treated guest destruction as logical page closure and canceled bound downloads. An exact-source before/after control confirmed that difference with an EventEmitter guest and controlled DownloadItem. Guest retirement now releases guest-owned callbacks while preserving ongoing page downloads, their destinations and cancel authorization. A retained numeric renderer route drains after the last download settles, provided no replacement guest, other download or newer routing owner needs it. + +Nine additional controls cover progress and completion/error delivery, explicit close after guest destruction, multiple downloads, replacement guests/routing, repeated guest destruction and renderer loss. Together with four existing browser suites, 55 tests pass; Node typecheck and ordinary/anti-slop lint pass. These controls do not establish native Chromium download survival after destruction on each operating system. No download capacity or wire format changes. + +## Reproduce + +With dependencies already installed, run from the repository root: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node docs/audits/browser-destroyed-guest-retention/reproduce.mjs +``` + +The script runs the actual manager and guest callback installers with EventEmitter WebContents fixtures. It removes only the destruction guard in memory for the baseline, then runs the same nine tests on the fixed source. A temporary test observer records actual map sizes before each assertion. It launches no Orca window or browser process, limits each worker to 512 MiB and each run to 60 seconds, uses the shared process launcher, and removes temporary files. Results include source hashes and runtime provenance. + +| After 1,000 distinct guest destructions | Before | Fixed | +| -------------------------------------------------------------------------- | -----: | ----: | +| Primary guest lookup | 0 | 0 | +| Each context-menu / grab-shortcut / app-shortcut / wheel cleanup map | 1,000 | 0 | +| Each renderer / workspace / worktree / profile map | 1,000 | 0 | +| Policy cleanup map | 0 | 0 | +| Each context-menu / grab-shortcut / app-shortcut map after `unregisterAll` | 1,000 | 0 | +| Renderer and workspace maps after `unregisterAll` | 1,000 | 0 | + +Baseline: six tests pass, three fail. Fixed: all nine pass. Controls cover explicit unregister, same-ID replacement with a captured old callback, popup and pre-registration destruction, renderer-process recovery, fresh guest registration, and two pages sharing one browser session profile. The selected existing browser-manager and offscreen lifecycle suites also passed: 64 tests across seven files including the new suite. + +## Version and limits + +Targeted source reads of `v1.4.198` confirm the same destroyed-event policy-only cleanup, map ownership, and `unregisterAll` omission. The executable comparison uses current production source; it does not launch the historical app. This is a retaining path present in the version reported by #19831/#19768. It does not establish that either incident followed this destruction sequence, or measure native memory retained by a destroyed WebContents. The 1,000 iterations measure retained callbacks and metadata, not 1,000 surviving Chromium processes or a gigabyte allocation. + +Adjacent audit negatives: explicit page close removes the renderer guest registry and main registration; worktree switching deliberately parks guests under the existing hidden-worktree retention policy; offscreen creation is synchronously indexed with shutdown admission and exact-window teardown; client-hosted async page creation checks availability after acquisitions and cleans canceled owners. PDF capture rejects late disconnected-client completion, its stream buffers have a five-minute TTL, and existing screenshot commands have deadlines. No additional native screenshot hang or unbounded native-page acquisition was reproduced. The separate late renderer registration reply can restore small page-ID metadata after close; it is outside this main-process fix. diff --git a/docs/audits/browser-destroyed-guest-retention/reproduce.mjs b/docs/audits/browser-destroyed-guest-retention/reproduce.mjs new file mode 100644 index 00000000000..f1dd27e8f04 --- /dev/null +++ b/docs/audits/browser-destroyed-guest-retention/reproduce.mjs @@ -0,0 +1,168 @@ +import { createHash } from 'node:crypto' +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { createRequire } from 'node:module' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import { build } from 'esbuild' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1.') +} + +const root = fileURLToPath(new URL('../../../', import.meta.url)) +const productionPath = 'src/main/browser/browser-manager-guest-navigation-policy.ts' +const testPath = 'src/main/browser/browser-manager-destroyed-guest.test.ts' +const fixturePath = 'src/main/browser/browser-manager-destroyed-guest-test-fixture.ts' +const current = await readFile(resolve(root, productionPath), 'utf8') +const fix = ` const browserTabId = this.tabIdByWebContentsId.get(guest.id) + // A destroyed primary guest also owns per-page callbacks that capture its WebContents. + if (browserTabId && this.webContentsIdByTabId.get(browserTabId) === guest.id) { + this.unregisterGuest(browserTabId, 'guest-destroyed') + return + } +` +if (current.split(fix).length !== 2) { + throw new Error('Expected exactly one primary-guest destruction guard; review the transform.') +} +const baseline = current.replace(fix, '') +const test = await readFile(resolve(root, testPath), 'utf8') +const observe = ' const counts = manager.retainedCounts()\n' +if (test.split(observe).length !== 2) { + throw new Error('Expected exactly one retained-count observer; review the transform.') +} +const observedTest = `import { appendFileSync } from 'node:fs'\n${test.replace( + observe, + `${observe} appendFileSync(process.env.ORCA_BROWSER_GUEST_COUNTS_PATH, JSON.stringify({ test: expect.getState().currentTestName, counts }) + '\\n')\n` +)}` +const sha256 = (source) => createHash('sha256').update(source).digest('hex') +const sourceHashes = { + [productionPath]: { before: sha256(baseline), after: sha256(current) }, + [testPath]: { current: sha256(test), observed: sha256(observedTest) }, + [fixturePath]: { current: sha256(await readFile(resolve(root, fixturePath))) } +} +for (const path of [ + 'src/main/browser/browser-manager-state.ts', + 'src/main/browser/browser-manager-registration.ts', + 'src/main/browser/browser-manager-download-lifecycle.ts' +]) { + sourceHashes[path] = { current: sha256(await readFile(resolve(root, path))) } +} +const scratch = await mkdtemp(join(tmpdir(), 'orca-browser-destroyed-guest-')) +const require = createRequire(import.meta.url) +let runnerModuleId + +try { + const runnerPath = join(scratch, 'run-process.cjs') + await build({ + absWorkingDir: root, + entryPoints: [resolve(root, 'src/shared/child-process/run-process.ts')], + outfile: runnerPath, + bundle: true, + platform: 'node', + format: 'cjs', + logLevel: 'silent' + }) + runnerModuleId = require.resolve(runnerPath) + const { runProcess } = require(runnerModuleId) + const configImport = JSON.stringify(pathToFileURL(resolve(root, 'config/vitest.config.ts')).href) + + async function run(label, production) { + const config = join(scratch, `${label}.config.mjs`) + const report = join(scratch, `${label}.json`) + const countsPath = join(scratch, `${label}.counts.jsonl`) + const sources = { + [resolve(root, productionPath).replaceAll('\\', '/')]: production, + [resolve(root, testPath).replaceAll('\\', '/')]: observedTest + } + await writeFile( + config, + `import base from ${configImport}; +const sources = ${JSON.stringify(sources)}; +export default {...base, test: {...base.test, include: [${JSON.stringify(testPath)}], maxWorkers: 1}, plugins: [{ + name: 'browser-destroyed-guest-audit', enforce: 'pre', + transform(_code, id) { + const source = sources[id.replaceAll('\\\\', '/').split('?')[0]]; + return source === undefined ? null : {code: source, map: null}; + } +}]};\n` + ) + const result = await runProcess({ + program: process.execPath, + args: [ + resolve(root, 'node_modules/vitest/vitest.mjs'), + 'run', + '--config', + config, + '--reporter=json', + `--outputFile=${report}` + ], + cwd: root, + env: { + ...process.env, + NODE_OPTIONS: '--max-old-space-size=512', + ORCA_BROWSER_GUEST_COUNTS_PATH: countsPath + }, + timeoutMs: 60_000, + maxOutputBytes: 2 * 1024 * 1024 + }) + let parsed + try { + parsed = JSON.parse(await readFile(report, 'utf8')) + } catch (error) { + throw new Error(`${label} failed: ${result.stderr || result.stdout}`, { cause: error }) + } + const counts = (await readFile(countsPath, 'utf8')) + .trim() + .split('\n') + .map((line) => JSON.parse(line)) + return { + exitCode: result.code, + passed: parsed.numPassedTests, + failed: parsed.numFailedTests, + counts, + failedCases: parsed.testResults.flatMap((suite) => + suite.assertionResults + .filter((assertion) => assertion.status === 'failed') + .map((assertion) => assertion.fullName) + ) + } + } + + const before = await run('before', baseline) + const after = await run('after', current) + const passed = + before.passed === 6 && + before.failed === 3 && + after.passed === 9 && + after.failed === 0 && + before.counts.length === 9 && + after.counts.length === 9 && + before.counts[0].counts.contextMenus === 1000 && + before.counts[1].counts.contextMenus === 1000 && + after.counts[0].counts.contextMenus === 0 && + after.counts[1].counts.contextMenus === 0 + console.log( + JSON.stringify( + { + comparison: + 'Actual BrowserManager registration, destroyed-event handler, callback maps and unregisterAll; Electron methods use EventEmitter fixtures. Baseline removes only the exact-primary destruction cleanup in a temporary source transform.', + provenance: { node: process.version, platform: process.platform, arch: process.arch }, + sourceHashes, + before, + after, + passed + }, + null, + 2 + ) + ) + if (!passed) { + process.exitCode = 1 + } +} finally { + if (runnerModuleId) { + delete require.cache[runnerModuleId] + } + await rm(scratch, { recursive: true, force: true }) +} diff --git a/docs/audits/browser-destroyed-guest-retention/results.json b/docs/audits/browser-destroyed-guest-retention/results.json new file mode 100644 index 00000000000..868b812529e --- /dev/null +++ b/docs/audits/browser-destroyed-guest-retention/results.json @@ -0,0 +1,321 @@ +{ + "comparison": "Actual BrowserManager registration, destroyed-event handler, callback maps and unregisterAll; Electron methods use EventEmitter fixtures. Baseline removes only the exact-primary destruction cleanup in a temporary source transform.", + "provenance": { + "node": "v26.6.0", + "platform": "darwin", + "arch": "arm64" + }, + "sourceHashes": { + "src/main/browser/browser-manager-guest-navigation-policy.ts": { + "before": "35aee2b5665a49535897fd0589853248f902061f77b3e142f94e90eabeb7332c", + "after": "741ac6d9f30fcf82993fa5c11f40093ba8a75483866407776c983538453f9b32" + }, + "src/main/browser/browser-manager-destroyed-guest.test.ts": { + "current": "25806188a208952a1bebd042ec0dc4552784179ed3b738a5d5336789604ef314", + "observed": "8e59825237dea1b53294361122f0ea681947d4e6e9ea8fa771b5e15e080d88a6" + }, + "src/main/browser/browser-manager-destroyed-guest-test-fixture.ts": { + "current": "4e6bf3589a3888860e6cfa8b6c83e8e50b4344650a5aaa85f221930ee8f9c0fd" + }, + "src/main/browser/browser-manager-state.ts": { + "current": "1d82e875461984b3bee2d9dc9b477be7518e9394ace77b24e9b513766cc8a210" + }, + "src/main/browser/browser-manager-registration.ts": { + "current": "55d66285b1d3ad29a7be596b1ca3536f333f2e3b2580e4d2a41090928ca8edd1" + }, + "src/main/browser/browser-manager-download-lifecycle.ts": { + "current": "8c8d0dd488d31f6098f7ea1000ed8d6ffd8b23704a097db974207c9732d77c2a" + } + }, + "before": { + "exitCode": 1, + "passed": 6, + "failed": 3, + "counts": [ + { + "test": "browser guest destruction ownership > releases registered callbacks and ownership after 1000 distinct guest destructions", + "counts": { + "guests": 0, + "contextMenus": 1000, + "grabShortcuts": 1000, + "appShortcuts": 1000, + "wheelHandlers": 1000, + "renderers": 1000, + "workspaces": 1000, + "worktrees": 1000, + "profiles": 1000, + "policies": 0 + } + }, + { + "test": "browser guest destruction ownership > leaves no dead-guest callbacks for the window-close unregisterAll path", + "counts": { + "guests": 0, + "contextMenus": 1000, + "grabShortcuts": 1000, + "appShortcuts": 1000, + "wheelHandlers": 0, + "renderers": 1000, + "workspaces": 1000, + "worktrees": 0, + "profiles": 0, + "policies": 0 + } + }, + { + "test": "browser guest destruction ownership > keeps explicit unregister before destruction idempotent", + "counts": { + "guests": 0, + "contextMenus": 0, + "grabShortcuts": 0, + "appShortcuts": 0, + "wheelHandlers": 0, + "renderers": 0, + "workspaces": 0, + "worktrees": 0, + "profiles": 0, + "policies": 0 + } + }, + { + "test": "browser guest destruction ownership > does not let a captured old destroyed callback retire a replacement guest", + "counts": { + "guests": 1, + "contextMenus": 1, + "grabShortcuts": 1, + "appShortcuts": 1, + "wheelHandlers": 1, + "renderers": 1, + "workspaces": 1, + "worktrees": 1, + "profiles": 1, + "policies": 1 + } + }, + { + "test": "browser guest destruction ownership > cleans a popup without retiring its live primary page", + "counts": { + "guests": 1, + "contextMenus": 1, + "grabShortcuts": 1, + "appShortcuts": 1, + "wheelHandlers": 1, + "renderers": 1, + "workspaces": 1, + "worktrees": 1, + "profiles": 1, + "policies": 1 + } + }, + { + "test": "browser guest destruction ownership > cleans policies for a guest destroyed before registration", + "counts": { + "guests": 0, + "contextMenus": 0, + "grabShortcuts": 0, + "appShortcuts": 0, + "wheelHandlers": 0, + "renderers": 0, + "workspaces": 0, + "worktrees": 0, + "profiles": 0, + "policies": 0 + } + }, + { + "test": "browser guest destruction ownership > preserves live guest ownership when its renderer process needs reload recovery", + "counts": { + "guests": 1, + "contextMenus": 1, + "grabShortcuts": 1, + "appShortcuts": 1, + "wheelHandlers": 1, + "renderers": 1, + "workspaces": 1, + "worktrees": 1, + "profiles": 1, + "policies": 1 + } + }, + { + "test": "browser guest destruction ownership > rebuilds ownership when a restored page registers its fresh guest", + "counts": { + "guests": 1, + "contextMenus": 1, + "grabShortcuts": 1, + "appShortcuts": 1, + "wheelHandlers": 1, + "renderers": 1, + "workspaces": 1, + "worktrees": 1, + "profiles": 1, + "policies": 1 + } + }, + { + "test": "browser guest destruction ownership > preserves a sibling page using the same browser session profile", + "counts": { + "guests": 1, + "contextMenus": 2, + "grabShortcuts": 2, + "appShortcuts": 2, + "wheelHandlers": 2, + "renderers": 2, + "workspaces": 2, + "worktrees": 2, + "profiles": 2, + "policies": 1 + } + } + ], + "failedCases": [ + "browser guest destruction ownership releases registered callbacks and ownership after 1000 distinct guest destructions", + "browser guest destruction ownership leaves no dead-guest callbacks for the window-close unregisterAll path", + "browser guest destruction ownership preserves a sibling page using the same browser session profile" + ] + }, + "after": { + "exitCode": 0, + "passed": 9, + "failed": 0, + "counts": [ + { + "test": "browser guest destruction ownership > releases registered callbacks and ownership after 1000 distinct guest destructions", + "counts": { + "guests": 0, + "contextMenus": 0, + "grabShortcuts": 0, + "appShortcuts": 0, + "wheelHandlers": 0, + "renderers": 0, + "workspaces": 0, + "worktrees": 0, + "profiles": 0, + "policies": 0 + } + }, + { + "test": "browser guest destruction ownership > leaves no dead-guest callbacks for the window-close unregisterAll path", + "counts": { + "guests": 0, + "contextMenus": 0, + "grabShortcuts": 0, + "appShortcuts": 0, + "wheelHandlers": 0, + "renderers": 0, + "workspaces": 0, + "worktrees": 0, + "profiles": 0, + "policies": 0 + } + }, + { + "test": "browser guest destruction ownership > keeps explicit unregister before destruction idempotent", + "counts": { + "guests": 0, + "contextMenus": 0, + "grabShortcuts": 0, + "appShortcuts": 0, + "wheelHandlers": 0, + "renderers": 0, + "workspaces": 0, + "worktrees": 0, + "profiles": 0, + "policies": 0 + } + }, + { + "test": "browser guest destruction ownership > does not let a captured old destroyed callback retire a replacement guest", + "counts": { + "guests": 1, + "contextMenus": 1, + "grabShortcuts": 1, + "appShortcuts": 1, + "wheelHandlers": 1, + "renderers": 1, + "workspaces": 1, + "worktrees": 1, + "profiles": 1, + "policies": 1 + } + }, + { + "test": "browser guest destruction ownership > cleans a popup without retiring its live primary page", + "counts": { + "guests": 1, + "contextMenus": 1, + "grabShortcuts": 1, + "appShortcuts": 1, + "wheelHandlers": 1, + "renderers": 1, + "workspaces": 1, + "worktrees": 1, + "profiles": 1, + "policies": 1 + } + }, + { + "test": "browser guest destruction ownership > cleans policies for a guest destroyed before registration", + "counts": { + "guests": 0, + "contextMenus": 0, + "grabShortcuts": 0, + "appShortcuts": 0, + "wheelHandlers": 0, + "renderers": 0, + "workspaces": 0, + "worktrees": 0, + "profiles": 0, + "policies": 0 + } + }, + { + "test": "browser guest destruction ownership > preserves live guest ownership when its renderer process needs reload recovery", + "counts": { + "guests": 1, + "contextMenus": 1, + "grabShortcuts": 1, + "appShortcuts": 1, + "wheelHandlers": 1, + "renderers": 1, + "workspaces": 1, + "worktrees": 1, + "profiles": 1, + "policies": 1 + } + }, + { + "test": "browser guest destruction ownership > rebuilds ownership when a restored page registers its fresh guest", + "counts": { + "guests": 1, + "contextMenus": 1, + "grabShortcuts": 1, + "appShortcuts": 1, + "wheelHandlers": 1, + "renderers": 1, + "workspaces": 1, + "worktrees": 1, + "profiles": 1, + "policies": 1 + } + }, + { + "test": "browser guest destruction ownership > preserves a sibling page using the same browser session profile", + "counts": { + "guests": 1, + "contextMenus": 1, + "grabShortcuts": 1, + "appShortcuts": 1, + "wheelHandlers": 1, + "renderers": 1, + "workspaces": 1, + "worktrees": 1, + "profiles": 1, + "policies": 1 + } + } + ], + "failedCases": [] + }, + "passed": true +} diff --git a/src/main/browser/browser-manager-destroyed-guest-downloads.test.ts b/src/main/browser/browser-manager-destroyed-guest-downloads.test.ts new file mode 100644 index 00000000000..28b228d6f43 --- /dev/null +++ b/src/main/browser/browser-manager-destroyed-guest-downloads.test.ts @@ -0,0 +1,152 @@ +import { afterEach, beforeEach, expect, it, vi } from 'vitest' + +const mocks = vi.hoisted(() => ({ contents: new Map() })) +vi.mock('electron', () => ({ + app: { getPath: () => '/downloads' }, + BrowserWindow: { fromWebContents: () => null }, + clipboard: { writeText: vi.fn() }, + shell: { openExternal: vi.fn() }, + Menu: { buildFromTemplate: vi.fn() }, + screen: { getCursorScreenPoint: () => ({ x: 0, y: 0 }) }, + webContents: { fromId: (id: number) => mocks.contents.get(id) ?? null } +})) +vi.mock('./popup-origin-bar-window', () => ({ openPopupWithOriginBar: vi.fn() })) + +import { + DestroyedGuestTestContents, + DestroyedGuestTestManager +} from './browser-manager-destroyed-guest-test-fixture' +import { createDownloadItem, getDownloadItemEventHandler } from './browser-manager-test-harness' + +const event = { preventDefault: () => {}, defaultPrevented: false } +const manager = new DestroyedGuestTestManager() +const renderer = { isDestroyed: vi.fn(() => false), send: vi.fn() } +let nextGuestId = 1 + +function register(rendererId = 5001): DestroyedGuestTestContents { + const guest = new DestroyedGuestTestContents(nextGuestId++) + mocks.contents.set(guest.id, guest.asWebContents()) + mocks.contents.set(rendererId, renderer) + manager.attachGuestPolicies(guest.asWebContents()) + expect( + manager.registerGuest({ + browserPageId: 'recoverable-page', + webContentsId: guest.id, + rendererWebContentsId: rendererId + }) + ).toBe(true) + return guest +} + +function download(guest: DestroyedGuestTestContents): Electron.DownloadItem { + const item = createDownloadItem() + manager.handleGuestWillDownload({ guestWebContentsId: guest.id, item }) + return item +} + +beforeEach(() => { + renderer.isDestroyed.mockReset().mockReturnValue(false) + renderer.send.mockClear() +}) +afterEach(() => { + manager.unregisterAll() + mocks.contents.clear() +}) + +it.each(['completed', 'cancelled', 'interrupted'] as const)( + 'preserves page download until native %s and then releases its routing entry', + (state) => { + const guest = register() + const item = download(guest) + guest.destroy() + expect(item.cancel).not.toHaveBeenCalled() + expect(manager.retainedCounts()).toMatchObject({ guests: 0, contextMenus: 0, renderers: 1 }) + getDownloadItemEventHandler(item, 'on', 'updated')?.(event, 'progressing') + expect(renderer.send).toHaveBeenCalledWith( + 'browser:download-progress', + expect.objectContaining({ browserPageId: 'recoverable-page' }) + ) + getDownloadItemEventHandler(item, 'once', 'done')?.(event, state) + expect(renderer.send).toHaveBeenCalledWith( + 'browser:download-finished', + expect.objectContaining({ + browserPageId: 'recoverable-page', + status: state === 'completed' ? 'completed' : state === 'cancelled' ? 'canceled' : 'failed' + }) + ) + expect(manager.downloadCount()).toBe(0) + expect(manager.retainedCounts().renderers).toBe(0) + } +) + +it('still cancels a download when its logical page closes after guest destruction', () => { + const guest = register() + const item = download(guest) + guest.destroy() + manager.unregisterGuest('recoverable-page') + expect(item.cancel).toHaveBeenCalledOnce() + expect(renderer.send).toHaveBeenCalledWith( + 'browser:download-finished', + expect.objectContaining({ status: 'canceled', error: 'Tab closed before download completed.' }) + ) + expect(manager.downloadCount()).toBe(0) + expect(manager.retainedCounts().renderers).toBe(0) +}) + +it('keeps progress routing until the last of multiple downloads settles', () => { + const guest = register() + const first = download(guest) + const second = download(guest) + guest.destroy() + getDownloadItemEventHandler(first, 'once', 'done')?.(event, 'completed') + expect(manager.downloadCount()).toBe(1) + expect(manager.retainedCounts().renderers).toBe(1) + getDownloadItemEventHandler(second, 'once', 'done')?.(event, 'completed') + expect(manager.downloadCount()).toBe(0) + expect(manager.retainedCounts().renderers).toBe(0) +}) + +it('keeps the replacement guest and its routing when an old download settles', () => { + const old = register() + const item = download(old) + old.destroy() + const replacement = register(5002) + getDownloadItemEventHandler(item, 'once', 'done')?.(event, 'completed') + expect(manager.getGuestWebContentsId('recoverable-page')).toBe(replacement.id) + expect(manager.retainedCounts().renderers).toBe(1) + expect(manager.downloadCount()).toBe(0) +}) + +it('releases current routing after two guest destructions and the final old download settles', () => { + const old = register() + const item = download(old) + old.destroy() + register(5002).destroy() + getDownloadItemEventHandler(item, 'once', 'done')?.(event, 'completed') + expect(manager.downloadCount()).toBe(0) + expect(manager.retainedCounts().renderers).toBe(0) +}) + +it('does not erase a replacement routing owner installed during completion delivery', () => { + const old = register() + const item = download(old) + old.destroy() + renderer.send.mockImplementationOnce(() => register(5002)) + getDownloadItemEventHandler(item, 'once', 'done')?.(event, 'completed') + expect(manager.retainedCounts().renderers).toBe(1) + manager.unregisterGuest('recoverable-page') + expect(manager.retainedCounts().renderers).toBe(0) +}) + +it('releases download state after renderer loss without sending to a destroyed renderer', () => { + const guest = register() + const item = download(guest) + guest.destroy() + renderer.isDestroyed.mockReturnValue(true) + renderer.send.mockClear() + getDownloadItemEventHandler(item, 'on', 'updated')?.(event, 'progressing') + getDownloadItemEventHandler(item, 'once', 'done')?.(event, 'completed') + expect(renderer.send).not.toHaveBeenCalled() + expect(manager.downloadCount()).toBe(0) + expect(manager.retainedCounts().renderers).toBe(0) +}) diff --git a/src/main/browser/browser-manager-destroyed-guest-test-fixture.ts b/src/main/browser/browser-manager-destroyed-guest-test-fixture.ts new file mode 100644 index 00000000000..3542a6c8b7e --- /dev/null +++ b/src/main/browser/browser-manager-destroyed-guest-test-fixture.ts @@ -0,0 +1,69 @@ +import { EventEmitter } from 'node:events' +import type { WebContents } from 'electron' +import { BrowserManager } from './browser-manager' + +const session = { getUserAgent: () => 'Chrome/140.0.0.0' } + +export class DestroyedGuestTestManager extends BrowserManager { + downloadCount(): number { + return this.downloadsById.size + } + + retainedCounts(): Record { + return { + guests: this.webContentsIdByTabId.size, + contextMenus: this.contextMenuCleanupByTabId.size, + grabShortcuts: this.grabShortcutCleanupByTabId.size, + appShortcuts: this.shortcutForwardingCleanupByTabId.size, + wheelHandlers: this.mouseWheelZoomCleanupByTabId.size, + renderers: this.rendererWebContentsIdByTabId.size, + workspaces: this.workspaceIdByPageId.size, + worktrees: this.worktreeIdByTabId.size, + profiles: this.sessionProfileIdByPageId.size, + policies: this.policyCleanupByGuestId.size + } + } +} + +export class DestroyedGuestTestContents extends EventEmitter { + readonly debugger = Object.assign(new EventEmitter(), { + isAttached: () => false, + sendCommand: async () => undefined + }) + readonly session = session + private destroyed = false + + constructor(readonly id: number) { + super() + } + + asWebContents(): WebContents { + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: The fixture provides the WebContents methods exercised by guest registration and teardown. + return this as unknown as WebContents + } + + isDestroyed(): boolean { + return this.destroyed + } + + getType(): string { + return 'webview' + } + + getURL(): string { + return 'https://example.test' + } + + getUserAgent(): string { + return session.getUserAgent() + } + + setUserAgent(): void {} + setWindowOpenHandler(): void {} + setBackgroundThrottling(): void {} + + destroy(): void { + this.destroyed = true + this.emit('destroyed') + } +} diff --git a/src/main/browser/browser-manager-destroyed-guest.test.ts b/src/main/browser/browser-manager-destroyed-guest.test.ts new file mode 100644 index 00000000000..faacbb56fc4 --- /dev/null +++ b/src/main/browser/browser-manager-destroyed-guest.test.ts @@ -0,0 +1,148 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' + +const mocks = vi.hoisted(() => ({ guests: new Map() })) +vi.mock('electron', () => ({ + app: { getPath: () => '/downloads' }, + BrowserWindow: { fromWebContents: () => null }, + clipboard: { writeText: vi.fn() }, + shell: { openExternal: vi.fn() }, + Menu: { buildFromTemplate: vi.fn() }, + screen: { getCursorScreenPoint: () => ({ x: 0, y: 0 }) }, + webContents: { fromId: (id: number) => mocks.guests.get(id) ?? null } +})) +vi.mock('./popup-origin-bar-window', () => ({ openPopupWithOriginBar: vi.fn() })) + +import { + DestroyedGuestTestContents, + DestroyedGuestTestManager +} from './browser-manager-destroyed-guest-test-fixture' + +describe('browser guest destruction ownership', () => { + const manager = new DestroyedGuestTestManager() + const pageIds = new Set() + const guests = new Set() + let nextId = 1 + + function createGuest(): DestroyedGuestTestContents { + const guest = new DestroyedGuestTestContents(nextId++) + guests.add(guest) + mocks.guests.set(guest.id, guest.asWebContents()) + return guest + } + + function register(pageId: string): DestroyedGuestTestContents { + const guest = createGuest() + pageIds.add(pageId) + manager.attachGuestPolicies(guest.asWebContents()) + expect( + manager.registerGuest({ + browserPageId: pageId, + webContentsId: guest.id, + rendererWebContentsId: 5001, + workspaceId: 'workspace-1', + worktreeId: 'worktree-1', + sessionProfileId: 'profile-1' + }) + ).toBe(true) + return guest + } + + function expectRetainedCount(count: number): void { + const counts = manager.retainedCounts() + expect(counts).toEqual(Object.fromEntries(Object.keys(counts).map((key) => [key, count]))) + } + + afterEach(() => { + for (const pageId of pageIds) { + manager.unregisterGuest(pageId) + } + manager.unregisterAll() + for (const guest of guests) { + guest.removeAllListeners() + } + pageIds.clear() + guests.clear() + mocks.guests.clear() + }) + + it('releases registered callbacks and ownership after 1000 distinct guest destructions', () => { + for (let index = 0; index < 1000; index++) { + register(`retired-${index}`).destroy() + } + expectRetainedCount(0) + }) + + it('leaves no dead-guest callbacks for the window-close unregisterAll path', () => { + for (let index = 0; index < 1000; index++) { + register(`window-${index}`).destroy() + } + manager.unregisterAll() + expectRetainedCount(0) + }) + + it('keeps explicit unregister before destruction idempotent', () => { + const guest = register('explicit') + manager.unregisterGuest('explicit') + guest.destroy() + manager.unregisterGuest('explicit') + expectRetainedCount(0) + }) + + it('does not let a captured old destroyed callback retire a replacement guest', () => { + const old = register('replacement') + const [oldDestroyed] = old.listeners('destroyed') + expect(oldDestroyed).toBeTypeOf('function') + const replacement = register('replacement') + oldDestroyed.call(old) + expect(manager.getGuestWebContentsId('replacement')).toBe(replacement.id) + expect(manager.getWorktreeIdForTab('replacement')).toBe('worktree-1') + expectRetainedCount(1) + }) + + it('cleans a popup without retiring its live primary page', () => { + const parent = register('popup-parent') + const popup = createGuest() + manager.attachGuestPolicies(popup.asWebContents(), { + rootGuestWebContentsId: parent.id, + browserTabId: 'popup-parent' + }) + popup.destroy() + expect(manager.getGuestWebContentsId('popup-parent')).toBe(parent.id) + expectRetainedCount(1) + }) + + it('cleans policies for a guest destroyed before registration', () => { + const guest = createGuest() + manager.attachGuestPolicies(guest.asWebContents()) + guest.destroy() + expectRetainedCount(0) + }) + + it('preserves live guest ownership when its renderer process needs reload recovery', () => { + const guest = register('renderer-recovery') + guest.emit('render-process-gone', {}, { reason: 'crashed', exitCode: 1 }) + expect(manager.getGuestWebContentsId('renderer-recovery')).toBe(guest.id) + expect(manager.getSessionProfileIdForTab('renderer-recovery')).toBe('profile-1') + expectRetainedCount(1) + }) + + it('rebuilds ownership when a restored page registers its fresh guest', () => { + register('fresh-owner').destroy() + const replacement = register('fresh-owner') + expect(manager.getGuestWebContentsId('fresh-owner')).toBe(replacement.id) + expect(manager.getWorktreeIdForTab('fresh-owner')).toBe('worktree-1') + expect(manager.getSessionProfileIdForTab('fresh-owner')).toBe('profile-1') + expectRetainedCount(1) + }) + + it('preserves a sibling page using the same browser session profile', () => { + const retiring = register('retiring') + const sibling = register('sibling') + expect(retiring.session).toBe(sibling.session) + retiring.destroy() + expect(manager.getGuestWebContentsId('sibling')).toBe(sibling.id) + expect(manager.getSessionProfileIdForTab('sibling')).toBe('profile-1') + expect(manager.getWorktreeIdForTab('sibling')).toBe('worktree-1') + expectRetainedCount(1) + }) +}) diff --git a/src/main/browser/browser-manager-download-lifecycle.ts b/src/main/browser/browser-manager-download-lifecycle.ts index 614b7651d16..ba45eed1d0d 100644 --- a/src/main/browser/browser-manager-download-lifecycle.ts +++ b/src/main/browser/browser-manager-download-lifecycle.ts @@ -34,6 +34,9 @@ export abstract class BrowserManagerDownloadLifecycle extends BrowserManagerDown if (!download) { return } + const rendererOwner = download.browserTabId + ? this.rendererWebContentsIdByTabId.get(download.browserTabId) + : undefined this.sendDownloadStarted(downloadId) if (download.receivedBytes > 0 || download.transientState) { this.sendDownloadProgress(download.browserTabId, { @@ -50,6 +53,7 @@ export abstract class BrowserManagerDownloadLifecycle extends BrowserManagerDown browserPageId: download.browserTabId ?? undefined }) this.downloadsById.delete(downloadId) + this.releaseRetiredDownloadRenderer(download.browserTabId, rendererOwner) } } @@ -150,6 +154,9 @@ export abstract class BrowserManagerDownloadLifecycle extends BrowserManagerDown if (!download) { return } + const rendererOwner = download.browserTabId + ? this.rendererWebContentsIdByTabId.get(download.browserTabId) + : undefined if (download.cleanup) { download.cleanup() @@ -169,6 +176,7 @@ export abstract class BrowserManagerDownloadLifecycle extends BrowserManagerDown } this.downloadsById.delete(downloadId) + this.releaseRetiredDownloadRenderer(download.browserTabId, rendererOwner) } protected finishDownloadInternal( @@ -180,6 +188,9 @@ export abstract class BrowserManagerDownloadLifecycle extends BrowserManagerDown if (!download || download.terminalEvent) { return } + const rendererOwner = download.browserTabId + ? this.rendererWebContentsIdByTabId.get(download.browserTabId) + : undefined if (download.cleanup) { download.cleanup() @@ -205,9 +216,29 @@ export abstract class BrowserManagerDownloadLifecycle extends BrowserManagerDown this.sendDownloadStarted(downloadId) this.sendDownloadFinished(download.browserTabId, event) this.downloadsById.delete(downloadId) + this.releaseRetiredDownloadRenderer(download.browserTabId, rendererOwner) } } + private releaseRetiredDownloadRenderer( + browserTabId: string | null, + rendererOwner: number | undefined + ): void { + if ( + !browserTabId || + this.webContentsIdByTabId.has(browserTabId) || + this.rendererWebContentsIdByTabId.get(browserTabId) !== rendererOwner + ) { + return + } + for (const download of this.downloadsById.values()) { + if (download.browserTabId === browserTabId) { + return + } + } + this.rendererWebContentsIdByTabId.delete(browserTabId) + } + protected cancelPendingDownloadsForGuest(guestWebContentsId: number): void { const pending = this.pendingDownloadIdsByGuestId.get(guestWebContentsId) this.pendingDownloadIdsByGuestId.delete(guestWebContentsId) diff --git a/src/main/browser/browser-manager-guest-navigation-policy.ts b/src/main/browser/browser-manager-guest-navigation-policy.ts index abacd268640..dbe54a8ba13 100644 --- a/src/main/browser/browser-manager-guest-navigation-policy.ts +++ b/src/main/browser/browser-manager-guest-navigation-policy.ts @@ -129,7 +129,12 @@ export abstract class BrowserManagerGuestNavigationPolicy extends BrowserManager guest.on('did-navigate', didNavigateHandler) guest.on('did-fail-load', didFailLoadHandler) const handleDestroyed = (): void => { - // Why: guests can die before renderer registration, else attach-time closures leak until shutdown. + const browserTabId = this.tabIdByWebContentsId.get(guest.id) + // A destroyed primary guest also owns per-page callbacks that capture its WebContents. + if (browserTabId && this.webContentsIdByTabId.get(browserTabId) === guest.id) { + this.unregisterGuest(browserTabId, 'guest-destroyed') + return + } this.cleanupGuestPolicyAttachment(guest.id) } guest.on('destroyed', handleDestroyed) diff --git a/src/main/browser/browser-manager-registration.ts b/src/main/browser/browser-manager-registration.ts index 4f6abbe67e9..816c0d61cb2 100644 --- a/src/main/browser/browser-manager-registration.ts +++ b/src/main/browser/browser-manager-registration.ts @@ -72,7 +72,10 @@ export abstract class BrowserManagerRegistration extends BrowserManagerGuestPoli return true } - unregisterGuest(browserTabId: string): void { + unregisterGuest( + browserTabId: string, + reason: 'page-closed' | 'guest-destroyed' = 'page-closed' + ): void { // Why the check on the exit door too: a document page withdraws by revoking its grant, never // through here, so its id arriving is misaddressed — and the cancel below would evict that // preview's live grab on the strength of it. @@ -108,10 +111,14 @@ export abstract class BrowserManagerRegistration extends BrowserManagerGuestPoli mouseWheelZoomCleanup() this.mouseWheelZoomCleanupByTabId.delete(browserTabId) } - // Why: downloads are per-tab chrome; closing the tab must cancel active writes, not orphan them. + let hasActiveDownloads = false for (const [downloadId, download] of this.downloadsById.entries()) { if (download.browserTabId === browserTabId && !download.terminalEvent) { - this.cancelDownloadInternal(downloadId, 'Tab closed before download completed.') + if (reason === 'page-closed') { + this.cancelDownloadInternal(downloadId, 'Tab closed before download completed.') + } else { + hasActiveDownloads = true + } } } const wcId = this.webContentsIdByTabId.get(browserTabId) @@ -119,7 +126,10 @@ export abstract class BrowserManagerRegistration extends BrowserManagerGuestPoli this.tabIdByWebContentsId.delete(wcId) } this.webContentsIdByTabId.delete(browserTabId) - this.rendererWebContentsIdByTabId.delete(browserTabId) + // A destroyed guest can recover in an open page while its downloads still report progress. + if (!hasActiveDownloads) { + this.rendererWebContentsIdByTabId.delete(browserTabId) + } this.workspaceIdByPageId.delete(browserTabId) this.sessionProfileIdByPageId.delete(browserTabId) this.worktreeIdByTabId.delete(browserTabId) diff --git a/src/main/browser/browser-manager-state.ts b/src/main/browser/browser-manager-state.ts index c30c5ae8f17..da7fa256b4e 100644 --- a/src/main/browser/browser-manager-state.ts +++ b/src/main/browser/browser-manager-state.ts @@ -75,7 +75,10 @@ export abstract class BrowserManagerState extends BrowserManagerViewportScrollSt ): void protected abstract cancelGrabOp(browserTabId: string, reason: BrowserGrabCancelReason): void protected abstract hasActiveGrabOp(browserTabId: string): boolean - protected abstract unregisterGuest(browserTabId: string): void + protected abstract unregisterGuest( + browserTabId: string, + reason?: 'page-closed' | 'guest-destroyed' + ): void protected abstract cancelDownloadInternal(downloadId: string, reason: string): void protected abstract bindDownloadToTab(downloadId: string, browserTabId: string): void protected abstract flushDownloadSnapshot(downloadId: string): void From 0e3acf577d7285057e039d99d2891799dbdc3fbb Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:12:02 -0700 Subject: [PATCH 050/168] fix: release consumed runtime RPC queue entries (#21131) Co-authored-by: m4air --- .../runtime-rpc-consumed-queue/README.md | 47 ++++ .../baseline.config.mjs | 30 +++ .../electron-extended-results.json | 108 ++++++++ .../electron-resolver-results.json | 10 + .../electron-results.json | 152 ++++++++++++ .../extended-controls.cjs | 230 ++++++++++++++++++ .../extended-results.json | 108 ++++++++ .../runtime-rpc-consumed-queue/fix.patch | 42 ++++ .../queue-source.cjs | 52 ++++ .../runtime-rpc-consumed-queue/reproduce.cjs | 109 +++++++++ .../resolver-controls.cjs | 55 +++++ .../resolver-results.json | 10 + .../runtime-rpc-consumed-queue/results.json | 152 ++++++++++++ .../source-versions.json | 9 + .../runtime-rpc-call-queue-retention.test.ts | 183 ++++++++++++++ src/shared/runtime-rpc-call-queue.ts | 9 +- 16 files changed, 1302 insertions(+), 4 deletions(-) create mode 100644 docs/audits/runtime-rpc-consumed-queue/README.md create mode 100644 docs/audits/runtime-rpc-consumed-queue/baseline.config.mjs create mode 100644 docs/audits/runtime-rpc-consumed-queue/electron-extended-results.json create mode 100644 docs/audits/runtime-rpc-consumed-queue/electron-resolver-results.json create mode 100644 docs/audits/runtime-rpc-consumed-queue/electron-results.json create mode 100644 docs/audits/runtime-rpc-consumed-queue/extended-controls.cjs create mode 100644 docs/audits/runtime-rpc-consumed-queue/extended-results.json create mode 100644 docs/audits/runtime-rpc-consumed-queue/fix.patch create mode 100644 docs/audits/runtime-rpc-consumed-queue/queue-source.cjs create mode 100644 docs/audits/runtime-rpc-consumed-queue/reproduce.cjs create mode 100644 docs/audits/runtime-rpc-consumed-queue/resolver-controls.cjs create mode 100644 docs/audits/runtime-rpc-consumed-queue/resolver-results.json create mode 100644 docs/audits/runtime-rpc-consumed-queue/results.json create mode 100644 docs/audits/runtime-rpc-consumed-queue/source-versions.json create mode 100644 src/shared/runtime-rpc-call-queue-retention.test.ts diff --git a/docs/audits/runtime-rpc-consumed-queue/README.md b/docs/audits/runtime-rpc-consumed-queue/README.md new file mode 100644 index 00000000000..69404df7a5e --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/README.md @@ -0,0 +1,47 @@ +# Completed RPC queue records remain reachable + +Status: reproduced before and after the product fix on Node 26 and installed Electron 43.7.0 in Node mode. + +`RuntimeRpcCallQueuePool` advances each lane's head without clearing the consumed array element. While any call keeps that selector active, completed records keep their `run`, `resolve`, `reject`, and signal fields reachable until that same lane compacts. Compaction requires more than 32 consumed entries and at least half the array consumed. Selector deletion also releases the arrays when every active and queued call finishes. + +The fix assigns `undefined` to the consumed slot in `takeForeground` and `takeBackground` and permits undefined in the two array element types. An active call remains owned by its promise cleanup closure. Queued entries, both lane heads, counts, admission thresholds, batching, foreground preference, and cancellation behavior stay intact. + +## Production reachability + +- Desktop `src/main/ipc/runtime-environment-call-queue.ts` owns a module singleton. `runtime-environment-transport-routing.ts::callRuntimeEnvironment` passes a closure capturing request params, environment, and optional orchestration envelope. Its normal timeout is 15 seconds; `status.get` bypasses this queue. +- Paired web `src/renderer/src/web/preload-api/web-runtime-session.ts` owns another module singleton. `web-runtime-calls.ts` and `web-filesystem-api.ts::captureWebFileMutationSession` pass closures capturing params, environment, and sometimes an explicit client. Web request timeout defaults to 30 seconds after connection readiness. +- Current production callers pass/default `retainedBytes` to zero. The nonzero byte values in the proof exercise the queue's accounting; they are not evidence that deployed callers account their object graphs here. +- These are remote/paired execution paths. The finding does not explain local-only #19831 from code reachability alone. + +Finite individual call duration does not guarantee that an old lane's consumed records disappear: overlapping calls in the other lane can keep the selector active. The extended proof completes 70 successive background calls while eight completed foreground payloads stay reachable before the fix; each background call finishes, and releasing the final call makes the selector idle and permits collection. + +This is retention beyond useful lifetime, bounded in record count by existing compaction/admission behavior. It is not proof of unlimited growth for one selector or of any reported incident's magnitude. The isolated payloads are bounded dummy arrays rather than a real network workload. + +## Proofs + +`reproduce.cjs` bundles the actual queue module with its actual imports. `queue-source.cjs` reconstructs the baseline in memory by reversing `fix.patch`; both baseline and current source hashes must match `source-versions.json`. Eight completed calls capture eight 1 MiB typed-array payloads, with one other call holding the selector active. Weak references remain live before and all clear after the fix. Foreground and background lanes, eventual compaction, and eventual idle cleanup are covered. Queue byte credit and queued-call count already equal zero during stale retention. At most eight payload MiB are intentionally live in each fixture; the process has a 128 MiB old-space limit and a ten-second deadline. + +`extended-controls.cjs` checks active payloads are retained until their call settles, cancellation releases queued payloads without waiting for unrelated active calls, synchronous failure releases only after compaction/idle before the fix, rolling cross-lane traffic, and a 140-call mixed burst with six cancellations. Both variants execute the same 134 remaining calls in FIFO lane order with foreground priority, then delete the idle selector. + +`resolver-controls.cjs` isolates the runtime's settled-promise resolver behavior without using the queue. Keeping native resolve functions can also keep settled results reachable in some runtime versions; this must be reported separately from input closures. + +Node and Electron results are stored separately. Node 26.6.0/V8 14.6 collects fresh response payloads even with the stale queue records. Installed Electron 43.7.0/Node 24.21.0/V8 15.0 retains all eight fresh response payloads before and releases them after the fix. The standalone resolver control reproduces the same difference: saving native resolve functions retains eight of eight payloads in Electron and zero in Node; releasing those functions permits collection in both. This control isolates runtime promise behavior without claiming all Electron versions or browser renderer modes behave identically. + +The original Node-only negative-control expectation for response retention failed under Electron. The baseline now records that result rather than assuming every V8 version releases settled results identically. The fixed variant must release responses in both environments. This proof does not measure the exact Electron 43.4.1 historical binary. + +Run from the worktree: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/runtime-rpc-consumed-queue/reproduce.cjs +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/runtime-rpc-consumed-queue/extended-controls.cjs +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/runtime-rpc-consumed-queue/resolver-controls.cjs +ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/shared/runtime-rpc-call-queue.test.ts src/shared/runtime-rpc-call-queue-retention.test.ts +``` + +The installed Electron binary can run the same scripts with `ELECTRON_RUN_AS_NODE=1`, `ORCA_BACKGROUND_LAUNCH=1`, and the same Node flags. No Electron app or window is created. No network, microphone, or affected-host data is used. No heap-snapshot tools are exposed in this session; the proof measures WeakRef reachability and bounded process counters instead of reading raw heap snapshots. + +The existing eight queue tests and six added retention/lifecycle tests pass with the fix. To reproduce the three retention failures against the reconstructed baseline, use `ORCA_BACKGROUND_LAUNCH=1 node --expose-gc node_modules/vitest/vitest.mjs run --config docs/audits/runtime-rpc-consumed-queue/baseline.config.mjs src/shared/runtime-rpc-call-queue.test.ts src/shared/runtime-rpc-call-queue-retention.test.ts`; the expected outcome is 11 passing tests and three failures, with exit code 1. Node and Web project typechecks and the changed-code quality gate passed during promotion. Explicit basic/type-aware lint also covers these otherwise ignored audit scripts. + +## Source identity + +The queue source before the fix, fetched `origin/main`, and `v1.4.198` all had SHA-256 `45921658a10ffeefe0247673227aab643d1b5251d24aa460244fadd5480fc349` at review. `source-versions.json` records exact baseline/current hashes and compared commit IDs. Availability in that release supports reachability analysis; it does not attribute an incident. diff --git a/docs/audits/runtime-rpc-consumed-queue/baseline.config.mjs b/docs/audits/runtime-rpc-consumed-queue/baseline.config.mjs new file mode 100644 index 00000000000..07ca46c4336 --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/baseline.config.mjs @@ -0,0 +1,30 @@ +import path from 'node:path' +import { createRequire } from 'node:module' +import { defineConfig, mergeConfig } from 'vitest/config' +import rootConfig from '../../../config/vitest.config.ts' + +const require = createRequire(import.meta.url) +const proof = require('./queue-source.cjs') +const sourcePath = path.resolve(import.meta.dirname, '../../..', proof.versions.sourcePath) +export default mergeConfig( + rootConfig, + defineConfig({ + plugins: [ + { + name: 'runtime-rpc-queue-baseline', + enforce: 'pre', + load(id) { + if (id === sourcePath) { + return proof.baselineSource + } + } + } + ], + test: { + include: [ + 'src/shared/runtime-rpc-call-queue.test.ts', + 'src/shared/runtime-rpc-call-queue-retention.test.ts' + ] + } + }) +) diff --git a/docs/audits/runtime-rpc-consumed-queue/electron-extended-results.json b/docs/audits/runtime-rpc-consumed-queue/electron-extended-results.json new file mode 100644 index 00000000000..390bb34c318 --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/electron-extended-results.json @@ -0,0 +1,108 @@ +{ + "node": "v24.21.0", + "electron": "43.7.0", + "versions": { + "sourcePath": "src/shared/runtime-rpc-call-queue.ts", + "baselineSha256": "45921658a10ffeefe0247673227aab643d1b5251d24aa460244fadd5480fc349", + "fixedSha256": "6afe6eb8acd9025d227960fafef77ae205a455c7f680b15ad0db5cdfee18c71d", + "matchingBaselineRefs": { + "origin/main": "77cd61df396f25ec91ee2d5ddcbd1f55aa94f818", + "v1.4.198": "e0826956fcfc532f5a1e55b5e081f2e57e553c43" + } + }, + "proofSha256": "af64f8b55f070d4a863ff6b8f456d518d977cc06400bdbbbba60eb2c5a455aa2", + "reports": [ + { + "candidate": false, + "case": "active-and-cancelled", + "lane": "foreground", + "activeRetainedUntilSettlement": true, + "cancelledReleasedBeforeActiveFinishes": true + }, + { + "candidate": false, + "case": "synchronous-failure", + "lane": "foreground", + "retainedWhileOtherCallActive": 1, + "retainedAfterIdle": 0 + }, + { + "candidate": false, + "case": "active-and-cancelled", + "lane": "background", + "activeRetainedUntilSettlement": true, + "cancelledReleasedBeforeActiveFinishes": true + }, + { + "candidate": false, + "case": "synchronous-failure", + "lane": "background", + "retainedWhileOtherCallActive": 1, + "retainedAfterIdle": 0 + }, + { + "candidate": false, + "case": "rolling-background-traffic", + "completedForegroundPayloads": 8, + "completedBackgroundCalls": 70, + "foregroundPayloadsStillRetained": 8, + "retainedAfterIdle": 0, + "everyBackgroundCallCompletes": true + }, + { + "candidate": false, + "case": "ordering-compaction-and-cancellation", + "completed": 134, + "cancelled": 6, + "foregroundBeforeBackground": true, + "fifoWithinEachLane": true, + "finalQueueCount": 0 + }, + { + "candidate": true, + "case": "active-and-cancelled", + "lane": "foreground", + "activeRetainedUntilSettlement": true, + "cancelledReleasedBeforeActiveFinishes": true + }, + { + "candidate": true, + "case": "synchronous-failure", + "lane": "foreground", + "retainedWhileOtherCallActive": 0, + "retainedAfterIdle": 0 + }, + { + "candidate": true, + "case": "active-and-cancelled", + "lane": "background", + "activeRetainedUntilSettlement": true, + "cancelledReleasedBeforeActiveFinishes": true + }, + { + "candidate": true, + "case": "synchronous-failure", + "lane": "background", + "retainedWhileOtherCallActive": 0, + "retainedAfterIdle": 0 + }, + { + "candidate": true, + "case": "rolling-background-traffic", + "completedForegroundPayloads": 8, + "completedBackgroundCalls": 70, + "foregroundPayloadsStillRetained": 0, + "retainedAfterIdle": 0, + "everyBackgroundCallCompletes": true + }, + { + "candidate": true, + "case": "ordering-compaction-and-cancellation", + "completed": 134, + "cancelled": 6, + "foregroundBeforeBackground": true, + "fifoWithinEachLane": true, + "finalQueueCount": 0 + } + ] +} diff --git a/docs/audits/runtime-rpc-consumed-queue/electron-resolver-results.json b/docs/audits/runtime-rpc-consumed-queue/electron-resolver-results.json new file mode 100644 index 00000000000..9e568d55264 --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/electron-resolver-results.json @@ -0,0 +1,10 @@ +{ + "node": "v24.21.0", + "electron": "43.7.0", + "v8": "15.0.245.31-electron.0", + "proofSha256": "725087b61b9d79bc2daff4fe45f52951c531dcf9d23137a4939508957848350e", + "payloads": 8, + "bytesPerPayload": 1048576, + "retainedWithResolveFunctions": 8, + "retainedAfterResolveFunctionsReleased": 0 +} diff --git a/docs/audits/runtime-rpc-consumed-queue/electron-results.json b/docs/audits/runtime-rpc-consumed-queue/electron-results.json new file mode 100644 index 00000000000..059aab505b5 --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/electron-results.json @@ -0,0 +1,152 @@ +{ + "node": "v24.21.0", + "electron": "43.7.0", + "versions": { + "sourcePath": "src/shared/runtime-rpc-call-queue.ts", + "baselineSha256": "45921658a10ffeefe0247673227aab643d1b5251d24aa460244fadd5480fc349", + "fixedSha256": "6afe6eb8acd9025d227960fafef77ae205a455c7f680b15ad0db5cdfee18c71d", + "matchingBaselineRefs": { + "origin/main": "77cd61df396f25ec91ee2d5ddcbd1f55aa94f818", + "v1.4.198": "e0826956fcfc532f5a1e55b5e081f2e57e553c43" + } + }, + "proofSha256": "79e415e5f7de6516b4e4bd8fc8ab0c6d0ac139efe996b8c39ef4cf7b8659bacb", + "reports": [ + { + "candidate": false, + "kind": "input", + "lane": "foreground", + "completed": 8, + "releaseByIdle": false, + "historyLength": 9, + "head": 9, + "retainedBeforeCompaction": 8, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 8378970, + "heapUsed": -303508 + } + }, + { + "candidate": false, + "kind": "input", + "lane": "background", + "completed": 8, + "releaseByIdle": false, + "historyLength": 8, + "head": 8, + "retainedBeforeCompaction": 8, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 8388608, + "heapUsed": -2300 + } + }, + { + "candidate": false, + "kind": "input", + "lane": "foreground", + "completed": 8, + "releaseByIdle": true, + "historyLength": 9, + "head": 9, + "retainedBeforeCompaction": 8, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 8388608, + "heapUsed": -860 + } + }, + { + "candidate": false, + "kind": "result", + "lane": "foreground", + "completed": 8, + "releaseByIdle": false, + "historyLength": 9, + "head": 9, + "retainedBeforeCompaction": 8, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 8388608, + "heapUsed": 14952 + } + }, + { + "candidate": true, + "kind": "input", + "lane": "foreground", + "completed": 8, + "releaseByIdle": false, + "historyLength": 9, + "head": 9, + "retainedBeforeCompaction": 0, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": -9748, + "heapUsed": -2608 + } + }, + { + "candidate": true, + "kind": "input", + "lane": "background", + "completed": 8, + "releaseByIdle": false, + "historyLength": 8, + "head": 8, + "retainedBeforeCompaction": 0, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 0, + "heapUsed": 4176 + } + }, + { + "candidate": true, + "kind": "input", + "lane": "foreground", + "completed": 8, + "releaseByIdle": true, + "historyLength": 9, + "head": 9, + "retainedBeforeCompaction": 0, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 0, + "heapUsed": 2496 + } + }, + { + "candidate": true, + "kind": "result", + "lane": "foreground", + "completed": 8, + "releaseByIdle": false, + "historyLength": 9, + "head": 9, + "retainedBeforeCompaction": 0, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 0, + "heapUsed": -3112 + } + } + ] +} diff --git a/docs/audits/runtime-rpc-consumed-queue/extended-controls.cjs b/docs/audits/runtime-rpc-consumed-queue/extended-controls.cjs new file mode 100644 index 00000000000..0ad4ebd311a --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/extended-controls.cjs @@ -0,0 +1,230 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const { load, collect, sha, versions } = require('./queue-source.cjs') + +const PAYLOAD_BYTES = 1024 * 1024 +function liveCall(queue, method = 'git.status') { + const hold = Promise.withResolvers() + return { + release: () => hold.resolve(), + settled: queue.enqueue('fixture', method, () => hold.promise) + } +} +function payloadCall(queue, method, hold, signal, throwSynchronously = false) { + const payload = new Uint8Array(PAYLOAD_BYTES) + payload[0] = 19 + const ref = new WeakRef(payload) + const settled = queue.enqueue( + 'fixture', + method, + () => { + if (throwSynchronously) { + throw new Error(`fixture failure ${payload[0]}`) + } + return hold.then(() => payload[0]) + }, + payload.byteLength, + signal + ) + return { ref, settled } +} +function liveCount(refs) { + return refs.filter((ref) => ref.deref() !== undefined).length +} + +async function checkActiveAndCancelled(Queue, candidate, lane) { + const queue = new Queue(1, 1) + const method = lane === 'foreground' ? 'terminal.send' : 'git.status' + const hold = Promise.withResolvers() + const active = payloadCall(queue, method, hold.promise) + const controller = new AbortController() + const queued = payloadCall(queue, method, Promise.resolve(), controller.signal) + const rejected = assert.rejects(queued.settled, { name: 'AbortError' }) + await collect() + assert.equal(liveCount([active.ref, queued.ref]), 2) + assert.equal(queue.retainedCallBytes, 2 * PAYLOAD_BYTES) + controller.abort() + await rejected + await collect() + assert.equal(liveCount([active.ref]), 1) + assert.equal(liveCount([queued.ref]), 0) + assert.equal(queue.retainedCallBytes, PAYLOAD_BYTES) + assert.equal(queue.queuedCallCount, 0) + hold.resolve() + assert.equal(await active.settled, 19) + await collect() + assert.equal(liveCount([active.ref, queued.ref]), 0) + assert.equal(queue.retainedCallBytes, 0) + assert.equal(queue.queues.size, 0) + return { + candidate, + case: 'active-and-cancelled', + lane, + activeRetainedUntilSettlement: true, + cancelledReleasedBeforeActiveFinishes: true + } +} + +async function checkFailure(Queue, candidate, lane) { + const queue = new Queue(3, 2) + const hold = liveCall(queue, 'terminal.send') + const method = lane === 'foreground' ? 'terminal.send' : 'git.status' + let failed = payloadCall(queue, method, Promise.resolve(), undefined, true) + const ref = failed.ref + await assert.rejects(failed.settled, { message: 'fixture failure 19' }) + failed = null + await collect() + const retained = liveCount([ref]) + assert.equal(retained, candidate ? 0 : 1) + assert.equal(queue.retainedCallBytes, 0) + hold.release() + await hold.settled + await collect() + assert.equal(liveCount([ref]), 0) + assert.equal(queue.queues.size, 0) + return { + candidate, + case: 'synchronous-failure', + lane, + retainedWhileOtherCallActive: retained, + retainedAfterIdle: 0 + } +} + +async function checkCrossLaneTraffic(Queue, candidate) { + const queue = new Queue(3, 2) + let current = liveCall(queue) + const refs = [] + for (let index = 0; index < 8; index++) { + const item = payloadCall(queue, 'terminal.send', Promise.resolve()) + refs.push(item.ref) + assert.equal(await item.settled, 19) + } + for (let index = 0; index < 70; index++) { + const next = liveCall(queue) + current.release() + await current.settled + current = next + } + await collect() + const retained = liveCount(refs) + assert.equal(retained, candidate ? 0 : 8) + assert.equal(queue.retainedCallBytes, 0) + assert.equal(queue.queues.get('fixture').active, 1) + assert.equal(queue.queues.get('fixture').foregroundHead, 8) + assert.equal(queue.queues.get('fixture').backgroundHead, 5) + current.release() + await current.settled + await collect() + assert.equal(liveCount(refs), 0) + assert.equal(queue.queues.size, 0) + return { + candidate, + case: 'rolling-background-traffic', + completedForegroundPayloads: 8, + completedBackgroundCalls: 70, + foregroundPayloadsStillRetained: retained, + retainedAfterIdle: 0, + everyBackgroundCallCompletes: true + } +} + +async function checkOrderAndCompaction(Queue, candidate) { + const queue = new Queue(1, 1) + const blocker = liveCall(queue, 'terminal.send') + const started = [] + const pending = [] + const controllers = [] + for (const lane of ['background', 'foreground']) { + for (let index = 0; index < 70; index++) { + const id = `${lane}:${index}` + const controller = new AbortController() + const promise = queue.enqueue( + 'fixture', + lane === 'background' ? 'git.status' : 'terminal.send', + async () => { + started.push(id) + return id + }, + 0, + controller.signal + ) + pending.push( + promise.then( + (value) => ({ value }), + (error) => ({ error: error.name }) + ) + ) + controllers.push({ id, controller }) + } + } + const cancelled = new Set([ + 'foreground:0', + 'foreground:35', + 'foreground:69', + 'background:0', + 'background:35', + 'background:69' + ]) + for (const { id, controller } of controllers) { + if (cancelled.has(id)) { + controller.abort() + } + } + assert.equal(queue.queuedCallCount, 134) + blocker.release() + await blocker.settled + const results = await Promise.all(pending) + const expected = ['foreground', 'background'] + .flatMap((lane) => Array.from({ length: 70 }, (_, index) => `${lane}:${index}`)) + .filter((id) => !cancelled.has(id)) + assert.deepEqual(started, expected) + assert.equal(results.filter((result) => result.error === 'AbortError').length, 6) + assert.equal(results.filter((result) => result.value !== undefined).length, 134) + await collect() + assert.equal(queue.queuedCallCount, 0) + assert.equal(queue.queues.size, 0) + return { + candidate, + case: 'ordering-compaction-and-cancellation', + completed: 134, + cancelled: 6, + foregroundBeforeBackground: true, + fifoWithinEachLane: true, + finalQueueCount: 0 + } +} + +async function main() { + const reports = [] + for (const candidate of [false, true]) { + const Queue = load(candidate) + for (const lane of ['foreground', 'background']) { + reports.push(await checkActiveAndCancelled(Queue, candidate, lane)) + reports.push(await checkFailure(Queue, candidate, lane)) + } + reports.push(await checkCrossLaneTraffic(Queue, candidate)) + reports.push(await checkOrderAndCompaction(Queue, candidate)) + } + const report = { + node: process.version, + electron: process.versions.electron ?? null, + versions, + proofSha256: sha(fs.readFileSync(__filename)), + reports + } + const resultName = process.versions.electron + ? 'electron-extended-results.json' + : 'extended-results.json' + fs.writeFileSync(path.join(__dirname, resultName), `${JSON.stringify(report, null, 2)}\n`) + console.log(JSON.stringify(report, null, 2)) +} +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) +setTimeout(() => { + console.error('fixture timeout') + process.exit(2) +}, 10000).unref() diff --git a/docs/audits/runtime-rpc-consumed-queue/extended-results.json b/docs/audits/runtime-rpc-consumed-queue/extended-results.json new file mode 100644 index 00000000000..c180564894e --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/extended-results.json @@ -0,0 +1,108 @@ +{ + "node": "v26.6.0", + "electron": null, + "versions": { + "sourcePath": "src/shared/runtime-rpc-call-queue.ts", + "baselineSha256": "45921658a10ffeefe0247673227aab643d1b5251d24aa460244fadd5480fc349", + "fixedSha256": "6afe6eb8acd9025d227960fafef77ae205a455c7f680b15ad0db5cdfee18c71d", + "matchingBaselineRefs": { + "origin/main": "77cd61df396f25ec91ee2d5ddcbd1f55aa94f818", + "v1.4.198": "e0826956fcfc532f5a1e55b5e081f2e57e553c43" + } + }, + "proofSha256": "af64f8b55f070d4a863ff6b8f456d518d977cc06400bdbbbba60eb2c5a455aa2", + "reports": [ + { + "candidate": false, + "case": "active-and-cancelled", + "lane": "foreground", + "activeRetainedUntilSettlement": true, + "cancelledReleasedBeforeActiveFinishes": true + }, + { + "candidate": false, + "case": "synchronous-failure", + "lane": "foreground", + "retainedWhileOtherCallActive": 1, + "retainedAfterIdle": 0 + }, + { + "candidate": false, + "case": "active-and-cancelled", + "lane": "background", + "activeRetainedUntilSettlement": true, + "cancelledReleasedBeforeActiveFinishes": true + }, + { + "candidate": false, + "case": "synchronous-failure", + "lane": "background", + "retainedWhileOtherCallActive": 1, + "retainedAfterIdle": 0 + }, + { + "candidate": false, + "case": "rolling-background-traffic", + "completedForegroundPayloads": 8, + "completedBackgroundCalls": 70, + "foregroundPayloadsStillRetained": 8, + "retainedAfterIdle": 0, + "everyBackgroundCallCompletes": true + }, + { + "candidate": false, + "case": "ordering-compaction-and-cancellation", + "completed": 134, + "cancelled": 6, + "foregroundBeforeBackground": true, + "fifoWithinEachLane": true, + "finalQueueCount": 0 + }, + { + "candidate": true, + "case": "active-and-cancelled", + "lane": "foreground", + "activeRetainedUntilSettlement": true, + "cancelledReleasedBeforeActiveFinishes": true + }, + { + "candidate": true, + "case": "synchronous-failure", + "lane": "foreground", + "retainedWhileOtherCallActive": 0, + "retainedAfterIdle": 0 + }, + { + "candidate": true, + "case": "active-and-cancelled", + "lane": "background", + "activeRetainedUntilSettlement": true, + "cancelledReleasedBeforeActiveFinishes": true + }, + { + "candidate": true, + "case": "synchronous-failure", + "lane": "background", + "retainedWhileOtherCallActive": 0, + "retainedAfterIdle": 0 + }, + { + "candidate": true, + "case": "rolling-background-traffic", + "completedForegroundPayloads": 8, + "completedBackgroundCalls": 70, + "foregroundPayloadsStillRetained": 0, + "retainedAfterIdle": 0, + "everyBackgroundCallCompletes": true + }, + { + "candidate": true, + "case": "ordering-compaction-and-cancellation", + "completed": 134, + "cancelled": 6, + "foregroundBeforeBackground": true, + "fifoWithinEachLane": true, + "finalQueueCount": 0 + } + ] +} diff --git a/docs/audits/runtime-rpc-consumed-queue/fix.patch b/docs/audits/runtime-rpc-consumed-queue/fix.patch new file mode 100644 index 00000000000..4a55e27feae --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/fix.patch @@ -0,0 +1,42 @@ +diff --git a/src/shared/runtime-rpc-call-queue.ts b/src/shared/runtime-rpc-call-queue.ts +index dcf4015078..7a3c184260 100644 +--- a/src/shared/runtime-rpc-call-queue.ts ++++ b/src/shared/runtime-rpc-call-queue.ts +@@ -30,9 +30,9 @@ type QueuedRuntimeCall = { + type RuntimeCallQueue = { + active: number + backgroundActive: number +- foreground: QueuedRuntimeCall[] ++ foreground: (QueuedRuntimeCall | undefined)[] + foregroundHead: number +- background: QueuedRuntimeCall[] ++ background: (QueuedRuntimeCall | undefined)[] + backgroundHead: number + } + +@@ -202,6 +202,7 @@ export class RuntimeRpcCallQueuePool { + return undefined + } + const call = queue.foreground[queue.foregroundHead] ++ queue.foreground[queue.foregroundHead] = undefined + queue.foregroundHead += 1 + this.queuedCallCount = Math.max(0, this.queuedCallCount - 1) + this.compactForeground(queue) +@@ -213,6 +214,7 @@ export class RuntimeRpcCallQueuePool { + return undefined + } + const call = queue.background[queue.backgroundHead] ++ queue.background[queue.backgroundHead] = undefined + queue.backgroundHead += 1 + this.queuedCallCount = Math.max(0, this.queuedCallCount - 1) + this.compactBackground(queue) +@@ -223,8 +225,7 @@ export class RuntimeRpcCallQueuePool { + if (queue.foregroundHead <= 32 || queue.foregroundHead * 2 < queue.foreground.length) { + return + } +- // Why: large remote-runtime refresh bursts can queue many calls; +- // head indexes avoid O(n) shift costs while compaction releases closures. ++ // Head indexes avoid repeated shifts; compaction bounds the consumed prefix. + queue.foreground.splice(0, queue.foregroundHead) + queue.foregroundHead = 0 + } diff --git a/docs/audits/runtime-rpc-consumed-queue/queue-source.cjs b/docs/audits/runtime-rpc-consumed-queue/queue-source.cjs new file mode 100644 index 00000000000..028b0a9b01c --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/queue-source.cjs @@ -0,0 +1,52 @@ +const assert = require('node:assert/strict') +const { createHash } = require('node:crypto') +const { readFileSync } = require('node:fs') +const path = require('node:path') +const Module = require('node:module') +const esbuild = require('esbuild') +const { applyPatch, parsePatch, reversePatch } = require('diff') +const versions = require('./source-versions.json') + +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function') +const root = path.resolve(__dirname, '../../..') +const readText = (file) => readFileSync(file, 'utf8').replace(/\r\n/g, '\n') +const sha = (value) => createHash('sha256').update(value).digest('hex') +const fixedSource = readText(path.join(root, versions.sourcePath)) +assert.equal(sha(fixedSource), versions.fixedSha256, 'Product source changed; review proof hashes') +const patches = parsePatch(readText(path.join(__dirname, 'fix.patch'))) +assert.equal(patches.length, 1) +const baselineSource = applyPatch(fixedSource, reversePatch(patches[0])) +assert.notEqual(baselineSource, false) +assert.equal(sha(baselineSource), versions.baselineSha256, 'Baseline reconstruction changed') + +function load(candidate) { + const build = esbuild.buildSync({ + stdin: { + contents: candidate ? fixedSource : baselineSource, + resolveDir: path.join(root, 'src/shared'), + sourcefile: versions.sourcePath, + loader: 'ts' + }, + platform: 'node', + format: 'cjs', + bundle: true, + packages: 'external', + write: false + }) + const filename = path.join(__dirname, 'bundled-queue.cjs') + const loaded = new Module(filename, module) + loaded.filename = filename + loaded.paths = Module._nodeModulePaths(__dirname) + loaded._compile(build.outputFiles[0].text, filename) + return loaded.exports.RuntimeRpcCallQueuePool +} + +async function collect() { + for (let round = 0; round < 3; round += 1) { + await new Promise((resolve) => setImmediate(resolve)) + global.gc() + } +} + +module.exports = { load, collect, sha, versions, baselineSource } diff --git a/docs/audits/runtime-rpc-consumed-queue/reproduce.cjs b/docs/audits/runtime-rpc-consumed-queue/reproduce.cjs new file mode 100644 index 00000000000..228f779620e --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/reproduce.cjs @@ -0,0 +1,109 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const { load, collect, sha, versions } = require('./queue-source.cjs') +async function postInput(queue, method, index) { + const data = new Uint8Array(1024 * 1024) + data[0] = index + const ref = new WeakRef(data) + await queue.enqueue('fixture', method, async () => data[0], data.byteLength) + return ref +} +async function postResult(queue, method, index) { + let ref + await queue.enqueue('fixture', method, async () => { + const data = new Uint8Array(1024 * 1024) + data[0] = index + ref = new WeakRef(data) + return { data } + }) + assert(ref) + return ref +} +async function lifetime(Queue, candidate, kind, lane, completed, releaseByIdle = false) { + const queue = new Queue(3, 2) + const hold = Promise.withResolvers() + const stuck = queue.enqueue('fixture', 'fixture.hold', () => hold.promise) + const method = lane === 'background' ? 'git.status' : 'fixture.echo' + const refs = [] + const before = process.memoryUsage() + for (let i = 0; i < completed; i++) { + refs.push(await (kind === 'input' ? postInput : postResult)(queue, method, i)) + } + await collect() + const retainedBeforeCompaction = refs.filter((ref) => ref.deref()).length + const { historyLength, head } = (() => { + const state = queue.queues.get('fixture') + assert(state) + return { historyLength: state[lane].length, head: state[`${lane}Head`] } + })() + assert.equal(queue.retainedCallBytes, 0) + assert.equal(queue.queuedCallCount, 0) + if (candidate || kind === 'input') { + assert.equal(retainedBeforeCompaction, candidate ? 0 : completed) + } + const afterCompleted = process.memoryUsage() + if (releaseByIdle) { + hold.resolve(0) + await stuck + } + const completionsUntilCompaction = releaseByIdle ? 0 : 33 - head + for (let i = 0; i < completionsUntilCompaction; i++) { + await queue.enqueue('fixture', method, async () => 0) + } + await collect() + const retainedAfterCompaction = refs.filter((ref) => ref.deref()).length + assert.equal(retainedAfterCompaction, 0) + hold.resolve(0) + await stuck + await collect() + assert.equal(queue.queues.size, 0) + return { + candidate, + kind, + lane, + completed, + releaseByIdle, + historyLength, + head, + retainedBeforeCompaction, + retainedAfterCompaction, + activeCreditBytesAfterCompleted: 0, + finalQueues: queue.queues.size, + memoryDelta: { + external: afterCompleted.external - before.external, + heapUsed: afterCompleted.heapUsed - before.heapUsed + } + } +} +async function main() { + const reports = [] + for (const candidate of [false, true]) { + const Queue = load(candidate) + for (const [kind, lane, completed, releaseByIdle] of [ + ['input', 'foreground', 8, false], + ['input', 'background', 8, false], + ['input', 'foreground', 8, true], + ['result', 'foreground', 8, false] + ]) { + reports.push(await lifetime(Queue, candidate, kind, lane, completed, releaseByIdle)) + console.log(JSON.stringify(reports.at(-1))) + } + } + const resultName = process.versions.electron ? 'electron-results.json' : 'results.json' + fs.writeFileSync( + path.join(__dirname, resultName), + `${JSON.stringify({ node: process.version, electron: process.versions.electron ?? null, versions, proofSha256: sha(fs.readFileSync(__filename)), reports }, null, 2)}\n` + ) +} +module.exports = { load, collect, sha } +if (require.main === module) { + main().catch((error) => { + console.error(error) + process.exitCode = 1 + }) + setTimeout(() => { + console.error('fixture timeout') + process.exit(2) + }, 10000).unref() +} diff --git a/docs/audits/runtime-rpc-consumed-queue/resolver-controls.cjs b/docs/audits/runtime-rpc-consumed-queue/resolver-controls.cjs new file mode 100644 index 00000000000..b10c4ab6bb1 --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/resolver-controls.cjs @@ -0,0 +1,55 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const { collect, sha } = require('./queue-source.cjs') + +async function postResult(keepers) { + let ref + await new Promise((resolve) => { + const payload = new Uint8Array(1024 * 1024) + payload[0] = 19 + ref = new WeakRef(payload) + keepers.push(resolve) + resolve({ payload }) + }) + assert(ref) + return ref +} +async function main() { + const keepers = [] + const refs = [] + for (let index = 0; index < 8; index++) { + refs.push(await postResult(keepers)) + } + await collect() + const retainedWithResolveFunctions = refs.filter((ref) => ref.deref() !== undefined).length + keepers.length = 0 + await collect() + const retainedAfterResolveFunctionsReleased = refs.filter( + (ref) => ref.deref() !== undefined + ).length + assert.equal(retainedAfterResolveFunctionsReleased, 0) + const report = { + node: process.version, + electron: process.versions.electron ?? null, + v8: process.versions.v8, + proofSha256: sha(fs.readFileSync(__filename)), + payloads: 8, + bytesPerPayload: 1024 * 1024, + retainedWithResolveFunctions, + retainedAfterResolveFunctionsReleased + } + const resultName = process.versions.electron + ? 'electron-resolver-results.json' + : 'resolver-results.json' + fs.writeFileSync(path.join(__dirname, resultName), `${JSON.stringify(report, null, 2)}\n`) + console.log(JSON.stringify(report, null, 2)) +} +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) +setTimeout(() => { + console.error('fixture timeout') + process.exit(2) +}, 10000).unref() diff --git a/docs/audits/runtime-rpc-consumed-queue/resolver-results.json b/docs/audits/runtime-rpc-consumed-queue/resolver-results.json new file mode 100644 index 00000000000..f434de12456 --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/resolver-results.json @@ -0,0 +1,10 @@ +{ + "node": "v26.6.0", + "electron": null, + "v8": "14.6.202.34-node.26", + "proofSha256": "725087b61b9d79bc2daff4fe45f52951c531dcf9d23137a4939508957848350e", + "payloads": 8, + "bytesPerPayload": 1048576, + "retainedWithResolveFunctions": 0, + "retainedAfterResolveFunctionsReleased": 0 +} diff --git a/docs/audits/runtime-rpc-consumed-queue/results.json b/docs/audits/runtime-rpc-consumed-queue/results.json new file mode 100644 index 00000000000..245433ca6a3 --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/results.json @@ -0,0 +1,152 @@ +{ + "node": "v26.6.0", + "electron": null, + "versions": { + "sourcePath": "src/shared/runtime-rpc-call-queue.ts", + "baselineSha256": "45921658a10ffeefe0247673227aab643d1b5251d24aa460244fadd5480fc349", + "fixedSha256": "6afe6eb8acd9025d227960fafef77ae205a455c7f680b15ad0db5cdfee18c71d", + "matchingBaselineRefs": { + "origin/main": "77cd61df396f25ec91ee2d5ddcbd1f55aa94f818", + "v1.4.198": "e0826956fcfc532f5a1e55b5e081f2e57e553c43" + } + }, + "proofSha256": "79e415e5f7de6516b4e4bd8fc8ab0c6d0ac139efe996b8c39ef4cf7b8659bacb", + "reports": [ + { + "candidate": false, + "kind": "input", + "lane": "foreground", + "completed": 8, + "releaseByIdle": false, + "historyLength": 9, + "head": 9, + "retainedBeforeCompaction": 8, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 8378970, + "heapUsed": -555304 + } + }, + { + "candidate": false, + "kind": "input", + "lane": "background", + "completed": 8, + "releaseByIdle": false, + "historyLength": 8, + "head": 8, + "retainedBeforeCompaction": 8, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 8388608, + "heapUsed": 6648 + } + }, + { + "candidate": false, + "kind": "input", + "lane": "foreground", + "completed": 8, + "releaseByIdle": true, + "historyLength": 9, + "head": 9, + "retainedBeforeCompaction": 8, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 8388608, + "heapUsed": 192 + } + }, + { + "candidate": false, + "kind": "result", + "lane": "foreground", + "completed": 8, + "releaseByIdle": false, + "historyLength": 9, + "head": 9, + "retainedBeforeCompaction": 0, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 0, + "heapUsed": 7744 + } + }, + { + "candidate": true, + "kind": "input", + "lane": "foreground", + "completed": 8, + "releaseByIdle": false, + "historyLength": 9, + "head": 9, + "retainedBeforeCompaction": 0, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": -9748, + "heapUsed": -9552 + } + }, + { + "candidate": true, + "kind": "input", + "lane": "background", + "completed": 8, + "releaseByIdle": false, + "historyLength": 8, + "head": 8, + "retainedBeforeCompaction": 0, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 0, + "heapUsed": -3808 + } + }, + { + "candidate": true, + "kind": "input", + "lane": "foreground", + "completed": 8, + "releaseByIdle": true, + "historyLength": 9, + "head": 9, + "retainedBeforeCompaction": 0, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 0, + "heapUsed": -4616 + } + }, + { + "candidate": true, + "kind": "result", + "lane": "foreground", + "completed": 8, + "releaseByIdle": false, + "historyLength": 9, + "head": 9, + "retainedBeforeCompaction": 0, + "retainedAfterCompaction": 0, + "activeCreditBytesAfterCompleted": 0, + "finalQueues": 0, + "memoryDelta": { + "external": 0, + "heapUsed": 2608 + } + } + ] +} diff --git a/docs/audits/runtime-rpc-consumed-queue/source-versions.json b/docs/audits/runtime-rpc-consumed-queue/source-versions.json new file mode 100644 index 00000000000..8fe55a755cf --- /dev/null +++ b/docs/audits/runtime-rpc-consumed-queue/source-versions.json @@ -0,0 +1,9 @@ +{ + "sourcePath": "src/shared/runtime-rpc-call-queue.ts", + "baselineSha256": "45921658a10ffeefe0247673227aab643d1b5251d24aa460244fadd5480fc349", + "fixedSha256": "6afe6eb8acd9025d227960fafef77ae205a455c7f680b15ad0db5cdfee18c71d", + "matchingBaselineRefs": { + "origin/main": "77cd61df396f25ec91ee2d5ddcbd1f55aa94f818", + "v1.4.198": "e0826956fcfc532f5a1e55b5e081f2e57e553c43" + } +} diff --git a/src/shared/runtime-rpc-call-queue-retention.test.ts b/src/shared/runtime-rpc-call-queue-retention.test.ts new file mode 100644 index 00000000000..838cb3b9e00 --- /dev/null +++ b/src/shared/runtime-rpc-call-queue-retention.test.ts @@ -0,0 +1,183 @@ +import { describe, expect, it } from 'vitest' +import { RuntimeRpcCallQueuePool } from './runtime-rpc-call-queue' + +async function collect(): Promise { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + for (let round = 0; round < 3; round += 1) { + await new Promise((resolve) => setImmediate(resolve)) + globalThis.gc() + } +} + +function gate(): { promise: Promise; release: () => void } { + let release = (): void => {} + const promise = new Promise((resolve) => { + release = resolve + }) + return { promise, release } +} + +function enqueuePayload( + queue: RuntimeRpcCallQueuePool, + method: string, + wait: Promise, + signal?: AbortSignal +): { ref: WeakRef; settled: Promise } { + const payload = new Uint8Array(1024 * 1024) + payload[0] = 19 + return { + ref: new WeakRef(payload), + settled: queue.enqueue( + 'runtime-a', + method, + async () => { + await wait + return payload[0]! + }, + payload.byteLength, + signal + ) + } +} + +async function completePayload( + queue: RuntimeRpcCallQueuePool, + method: string +): Promise> { + const { ref, settled } = enqueuePayload(queue, method, Promise.resolve()) + expect(await settled).toBe(19) + return ref +} + +describe('runtime RPC completed-call retention', () => { + it.each(['terminal.send', 'git.status'])( + 'releases completed %s inputs while another call keeps the selector active', + async (method) => { + const queue = new RuntimeRpcCallQueuePool(3, 2) + const blocker = gate() + const active = queue.enqueue('runtime-a', 'terminal.send', () => blocker.promise) + try { + const refs: WeakRef[] = [] + for (let index = 0; index < 8; index += 1) { + refs.push(await completePayload(queue, method)) + } + await collect() + expect(refs.filter((ref) => ref.deref() !== undefined)).toHaveLength(0) + } finally { + blocker.release() + await active + } + } + ) + + it.each(['terminal.send', 'git.status'])( + 'keeps an active %s input and releases a cancelled queued input', + async (method) => { + const queue = new RuntimeRpcCallQueuePool(1, 1) + const blocker = gate() + const active = enqueuePayload(queue, method, blocker.promise) + const controller = new AbortController() + const queued = enqueuePayload(queue, method, Promise.resolve(), controller.signal) + const rejected = expect(queued.settled).rejects.toMatchObject({ name: 'AbortError' }) + try { + await collect() + expect(active.ref.deref()?.[0]).toBe(19) + expect(queued.ref.deref()?.[0]).toBe(19) + controller.abort() + await rejected + await collect() + expect(active.ref.deref()?.[0]).toBe(19) + expect(queued.ref.deref() === undefined).toBe(true) + } finally { + controller.abort() + blocker.release() + await Promise.allSettled([active.settled, queued.settled]) + } + expect(await active.settled).toBe(19) + await collect() + expect(active.ref.deref() === undefined).toBe(true) + } + ) + + it('releases old foreground inputs during continuous finite background calls', async () => { + const queue = new RuntimeRpcCallQueuePool(3, 2) + const startBackground = (): { release: () => void; settled: Promise } => { + const wait = gate() + return { + release: wait.release, + settled: queue.enqueue('runtime-a', 'git.status', () => wait.promise) + } + } + let active = startBackground() + try { + const ref = await completePayload(queue, 'terminal.send') + for (let index = 0; index < 70; index += 1) { + const next = startBackground() + active.release() + await active.settled + active = next + } + await collect() + expect(ref.deref() === undefined).toBe(true) + } finally { + active.release() + await active.settled + } + }) + + it('preserves lane order and queued cancellation across compaction', async () => { + const queue = new RuntimeRpcCallQueuePool(1, 1) + const blocker = gate() + const active = queue.enqueue('runtime-a', 'terminal.send', () => blocker.promise) + const started: string[] = [] + const pending: Promise[] = [] + const cancelled = new Set([ + 'foreground:0', + 'foreground:35', + 'foreground:69', + 'background:0', + 'background:35', + 'background:69' + ]) + for (const lane of ['background', 'foreground']) { + for (let index = 0; index < 70; index += 1) { + const id = `${lane}:${index}` + const controller = new AbortController() + const settled = queue.enqueue( + 'runtime-a', + lane === 'background' ? 'git.status' : 'terminal.send', + async () => { + started.push(id) + return id + }, + 0, + controller.signal + ) + if (cancelled.has(id)) { + pending.push( + settled.catch((error: unknown) => { + expect(error).toMatchObject({ name: 'AbortError' }) + return 'cancelled' + }) + ) + controller.abort() + } else { + pending.push(settled) + } + } + } + blocker.release() + await active + const results = await Promise.all(pending) + const expected = ['foreground', 'background'] + .flatMap((lane) => Array.from({ length: 70 }, (_, index) => `${lane}:${index}`)) + .filter((id) => !cancelled.has(id)) + expect(started).toEqual(expected) + expect(results.filter((value) => value === 'cancelled')).toHaveLength(6) + expect(await queue.enqueue('runtime-a', 'terminal.send', async () => 'recovered')).toBe( + 'recovered' + ) + }) +}) diff --git a/src/shared/runtime-rpc-call-queue.ts b/src/shared/runtime-rpc-call-queue.ts index dcf40150789..7a3c1842602 100644 --- a/src/shared/runtime-rpc-call-queue.ts +++ b/src/shared/runtime-rpc-call-queue.ts @@ -30,9 +30,9 @@ type QueuedRuntimeCall = { type RuntimeCallQueue = { active: number backgroundActive: number - foreground: QueuedRuntimeCall[] + foreground: (QueuedRuntimeCall | undefined)[] foregroundHead: number - background: QueuedRuntimeCall[] + background: (QueuedRuntimeCall | undefined)[] backgroundHead: number } @@ -202,6 +202,7 @@ export class RuntimeRpcCallQueuePool { return undefined } const call = queue.foreground[queue.foregroundHead] + queue.foreground[queue.foregroundHead] = undefined queue.foregroundHead += 1 this.queuedCallCount = Math.max(0, this.queuedCallCount - 1) this.compactForeground(queue) @@ -213,6 +214,7 @@ export class RuntimeRpcCallQueuePool { return undefined } const call = queue.background[queue.backgroundHead] + queue.background[queue.backgroundHead] = undefined queue.backgroundHead += 1 this.queuedCallCount = Math.max(0, this.queuedCallCount - 1) this.compactBackground(queue) @@ -223,8 +225,7 @@ export class RuntimeRpcCallQueuePool { if (queue.foregroundHead <= 32 || queue.foregroundHead * 2 < queue.foreground.length) { return } - // Why: large remote-runtime refresh bursts can queue many calls; - // head indexes avoid O(n) shift costs while compaction releases closures. + // Head indexes avoid repeated shifts; compaction bounds the consumed prefix. queue.foreground.splice(0, queue.foregroundHead) queue.foregroundHead = 0 } From f90370fb6b072847f625b4e1291a4927fe3c5ec4 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:12:05 -0700 Subject: [PATCH 051/168] fix: detach aborted shared auth filesystem waits (#21135) Co-authored-by: m4air --- .../auth-filesystem-wait-retention/README.md | 32 + .../before.config.mjs | 24 + .../electron-results.json | 598 ++++++++++++++++++ .../auth-filesystem-wait-retention/fix.patch | 98 +++ .../node-results.json | 597 +++++++++++++++++ .../reproduce.cjs | 242 +++++++ .../settlement-order.cjs | 37 ++ .../source-versions.json | 11 + .../sources.cjs | 29 + .../validation.json | 61 ++ ...uth-filesystem-operation-retention.test.ts | 166 +++++ .../rate-limits/auth-filesystem-operation.ts | 32 +- src/shared/promise-settlement-waiters.test.ts | 37 ++ src/shared/promise-settlement-waiters.ts | 16 +- 14 files changed, 1959 insertions(+), 21 deletions(-) create mode 100644 docs/audits/auth-filesystem-wait-retention/README.md create mode 100644 docs/audits/auth-filesystem-wait-retention/before.config.mjs create mode 100644 docs/audits/auth-filesystem-wait-retention/electron-results.json create mode 100644 docs/audits/auth-filesystem-wait-retention/fix.patch create mode 100644 docs/audits/auth-filesystem-wait-retention/node-results.json create mode 100644 docs/audits/auth-filesystem-wait-retention/reproduce.cjs create mode 100644 docs/audits/auth-filesystem-wait-retention/settlement-order.cjs create mode 100644 docs/audits/auth-filesystem-wait-retention/source-versions.json create mode 100644 docs/audits/auth-filesystem-wait-retention/sources.cjs create mode 100644 docs/audits/auth-filesystem-wait-retention/validation.json create mode 100644 src/main/rate-limits/auth-filesystem-operation-retention.test.ts diff --git a/docs/audits/auth-filesystem-wait-retention/README.md b/docs/audits/auth-filesystem-wait-retention/README.md new file mode 100644 index 00000000000..fb97929d597 --- /dev/null +++ b/docs/audits/auth-filesystem-wait-retention/README.md @@ -0,0 +1,32 @@ +# Release aborted shared auth filesystem waits + +When a filesystem operation remains pending, later Codex/Kimi quota polls reuse it and wait with new deadlines. The old waiter uses `Promise.race` for every poll. On the installed Electron runtime, each abandoned race retains its rejection reason until the raw operation settles, despite removing its abort listener. The fix uses the existing `PromiseSettlementWaiters` registry, which attaches one raw-result reaction and removes expired waiters. + +The original ownership symbols, last-waiter cancellation finalizer, one-raw-operation behavior, live callers, and future reads of a late result are preserved. Auth opts into deferred abort settlement so an already-queued raw result keeps its `Promise.race` priority; 24 success/failure/abort schedules compare equal before and after. Existing registry consumers keep their immediate-abort behavior. The abort factory type accepts `unknown` so false, zero, strings, and objects retain the existing auth rejection semantics. No admission or timeout limit changes. + +## Reproduce + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/auth-filesystem-wait-retention/reproduce.cjs +``` + +For Electron, run the installed Electron executable with `ELECTRON_RUN_AS_NODE=1` and the same arguments/environment. This launches no window. The proof reconstructs the original sources by reversing `fix.patch`; expected baseline hashes in `source-versions.json` make source drift fail. Both versions run the actual production scheduler/waiter code, with only the raw filesystem operation replaced by one manually settled promise. A 15-second deadline fails stalled proof execution. + +| Runtime / source | Plain aborted Errors alive while raw result pending | Amplified payload objects alive | After raw result settles | After owner drops | +| ---------------------------------------- | --------------------------------------------------- | ------------------------------- | ------------------------ | ----------------- | +| Node 26.6.0 / original | 1 of 128 | 1 of 128 | 1 | 0 | +| Node 26.6.0 / fixed | 1 of 128 | 1 of 128 | 1 | 0 | +| Electron 43.7.0, Node 24.21.0 / original | 128 of 128 | 128 of 128 | 1 | 0 | +| Electron 43.7.0, Node 24.21.0 / fixed | 1 of 128 | 1 of 128 | 1 | 0 | + +The one remaining reason belongs to the existing cancellation controller's first abort. Dropping the shared operation releases it. AbortController objects and abort listeners are released by both versions. The amplified arm attaches a **synthetic 64 KiB Uint8Array** to each Error, at most 8 MiB per case. Ordinary timeout errors are much smaller; the separate plain-Error arm verifies that artificial bytes are not needed to reproduce retention. Runtime differences are measured, without attributing them to a particular V8 change. + +Controls check an already-aborted first caller never starting raw work, raw rejection identity, aborted caller identity, future callers receiving late and already-settled results, a live sibling surviving cancellation, arbitrary abort reasons, and removed listeners. The unit regression additionally checks that repeated expired waits add no raw-result reactions and that their plain Error objects are collectible while a live caller still needs the operation. + +Validation: 50 auth/registry tests pass, including 18 new cases; the reverse-patch configuration produces two expected failures and 48 passes. Six existing watcher-consumer suites pass another 39 tests. Node, Web, and CLI typechecks, focused ordinary/type-aware lint, formatting, and the changed-code quality gate pass. `validation.json` records the test paths and scope. To run the prior implementation against the current tests, use `--config docs/audits/auth-filesystem-wait-retention/before.config.mjs` with those six auth/registry test paths. + +## Scope and limits + +The three production consumers are `codex-auth-presence.ts`, `codex-backend-auth.ts`, and `kimi-fetcher.ts`. Each intentionally retains the shared operation until actual filesystem settlement to avoid stacking native requests when UNC/WSL reads stall. This audit does not reproduce a real filesystem stall, historical Electron binary behavior, or an affected-host workload. + +The auth source matches `v1.4.198` and the audited main revision; the existing registry also matches main. The earlier broad accumulator PR #10179, reverted by #10255, added path/waiter/admission limits to this module. This change instead removes abandoned wait reactions and introduces no such limits. Nothing here attributes #19831 or #19768 to this mechanism or claims an incident-scale memory slope. diff --git a/docs/audits/auth-filesystem-wait-retention/before.config.mjs b/docs/audits/auth-filesystem-wait-retention/before.config.mjs new file mode 100644 index 00000000000..3f9a6aa4360 --- /dev/null +++ b/docs/audits/auth-filesystem-wait-retention/before.config.mjs @@ -0,0 +1,24 @@ +import { resolve } from 'node:path' +import { createRequire } from 'node:module' +import { defineConfig, mergeConfig } from 'vitest/config' +import baseConfig from '../../../config/vitest.config.ts' + +const loadSources = createRequire(import.meta.url)( + resolve('docs/audits/auth-filesystem-wait-retention/sources.cjs') +) +const { before } = loadSources() +export default mergeConfig( + baseConfig, + defineConfig({ + plugins: [ + { + name: 'auth-wait-before-fix', + enforce: 'pre', + transform(_code, id) { + const source = before.get(resolve(id.split('?')[0])) + return source === undefined ? undefined : { code: source, map: null } + } + } + ] + }) +) diff --git a/docs/audits/auth-filesystem-wait-retention/electron-results.json b/docs/audits/auth-filesystem-wait-retention/electron-results.json new file mode 100644 index 00000000000..1fa3f9e51d7 --- /dev/null +++ b/docs/audits/auth-filesystem-wait-retention/electron-results.json @@ -0,0 +1,598 @@ +{ + "sourceHashes": { + "src/main/rate-limits/auth-filesystem-operation.ts": { + "before": "581d7045220e92602ca83ec335576889dcba5ada4a699e866ea1dba10afbecac", + "after": "bfac9dc25c3f3c36ab590bc6e01be39ec19dea5a872f5c409ed149e65258f9e2" + }, + "src/shared/promise-settlement-waiters.ts": { + "before": "b0b60bae6dcca4bb4f335ef7dda250f4350aa5b0306d3595b887cc4af4493060", + "after": "92618db591cf1fc1ad52f257cc9f3314310bf902fbd24e5d18abcf93afb8e722" + } + }, + "runtime": { + "node": "24.21.0", + "acorn": "8.18.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.2", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "148", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "0.0.0", + "simdjson": "4.6.7", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2025c", + "undici": "7.29.1", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "15.0.245.31-electron.0", + "zlib": "1.3.2.1-motley", + "zstd": "1.6.0", + "electron": "43.7.0", + "chrome": "150.0.7871.250" + }, + "scenario": "128 aborted waits on one already-started unresolved operation; amplified arm has 64 KiB synthetic payload per abort Error; real native filesystem stall not reproduced", + "before": { + "cases": [ + { + "amplify": false, + "abortedWaits": 128, + "unresolved": { + "reasons": 128, + "controllers": 0, + "payloads": 0, + "rawCalls": 1 + }, + "settled": { + "reasons": 1, + "controllers": 0, + "payloads": 0 + }, + "dropped": { + "reasons": 0, + "controllers": 0, + "payloads": 0 + } + }, + { + "amplify": true, + "abortedWaits": 128, + "unresolved": { + "reasons": 128, + "controllers": 0, + "payloads": 128, + "rawCalls": 1 + }, + "settled": { + "reasons": 1, + "controllers": 0, + "payloads": 1 + }, + "dropped": { + "reasons": 0, + "controllers": 0, + "payloads": 0 + } + } + ], + "controls": { + "lateResultDelivered": true, + "settledResultDelivered": true, + "oneRawCall": 1, + "rawRejectionPreserved": true, + "preAbortedRawCalls": 0, + "allAbortListenersRemoved": true, + "arbitraryAbortReasonsPreserved": 4, + "liveSiblingSurvivesAbort": true + }, + "settlementOrder": [ + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 1, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 2, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 3, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 4, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 5, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 1, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 2, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 3, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 4, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 5, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 1, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 2, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 3, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 4, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 5, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 1, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 2, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 3, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 4, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 5, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + } + ], + "importedHashes": { + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + }, + "bundleSha256": "f895e71ca0f2102e7ff36fa133ae2065fa9810769d7ee6059c826f1a72bcc122" + }, + "after": { + "cases": [ + { + "amplify": false, + "abortedWaits": 128, + "unresolved": { + "reasons": 1, + "controllers": 0, + "payloads": 0, + "rawCalls": 1 + }, + "settled": { + "reasons": 1, + "controllers": 0, + "payloads": 0 + }, + "dropped": { + "reasons": 0, + "controllers": 0, + "payloads": 0 + } + }, + { + "amplify": true, + "abortedWaits": 128, + "unresolved": { + "reasons": 1, + "controllers": 0, + "payloads": 1, + "rawCalls": 1 + }, + "settled": { + "reasons": 1, + "controllers": 0, + "payloads": 1 + }, + "dropped": { + "reasons": 0, + "controllers": 0, + "payloads": 0 + } + } + ], + "controls": { + "lateResultDelivered": true, + "settledResultDelivered": true, + "oneRawCall": 1, + "rawRejectionPreserved": true, + "preAbortedRawCalls": 0, + "allAbortListenersRemoved": true, + "arbitraryAbortReasonsPreserved": 4, + "liveSiblingSurvivesAbort": true + }, + "settlementOrder": [ + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 1, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 2, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 3, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 4, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 5, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 1, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 2, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 3, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 4, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 5, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 1, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 2, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 3, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 4, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 5, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 1, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 2, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 3, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 4, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 5, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + } + ], + "importedHashes": { + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + }, + "bundleSha256": "04324b281a776aa1018995a36d11ff7cf56b4dd737761c3d5b0fb1ebc0047652" + } +} diff --git a/docs/audits/auth-filesystem-wait-retention/fix.patch b/docs/audits/auth-filesystem-wait-retention/fix.patch new file mode 100644 index 00000000000..dce5d0187de --- /dev/null +++ b/docs/audits/auth-filesystem-wait-retention/fix.patch @@ -0,0 +1,98 @@ +diff --git a/src/main/rate-limits/auth-filesystem-operation.ts b/src/main/rate-limits/auth-filesystem-operation.ts +index 228234e92c..7d030ab01e 100644 +--- a/src/main/rate-limits/auth-filesystem-operation.ts ++++ b/src/main/rate-limits/auth-filesystem-operation.ts +@@ -1,4 +1,5 @@ + import { parseWslUncPath } from '../../shared/wsl-paths' ++import { PromiseSettlementWaiters } from '../../shared/promise-settlement-waiters' + + const MAX_CONCURRENT_WSL_AUTH_OPERATIONS = 2 + const activeWslOperationDistros = new Set() +@@ -139,10 +140,9 @@ export function createAuthFilesystemOperation( + const waiters = new Set() + let settled = false + const result = scheduleAuthFilesystemOperation(authPath, neededController.signal, operation) +- const markSettled = (): void => { ++ const settlementWaiters = new PromiseSettlementWaiters(result, () => { + settled = true +- } +- void result.then(markSettled, markSettled) ++ }) + + return { + result, +@@ -156,20 +156,18 @@ export function createAuthFilesystemOperation( + + const waiter = Symbol('auth-filesystem-waiter') + waiters.add(waiter) +- let onAbort: (() => void) | null = null +- const aborted = new Promise((_resolve, reject) => { +- onAbort = () => reject(getAbortReason(signal)) +- signal.addEventListener('abort', onAbort, { once: true }) +- }) +- return Promise.race([result, aborted]).finally(() => { +- if (onAbort) { +- signal.removeEventListener('abort', onAbort) +- } +- waiters.delete(waiter) +- if (!settled && waiters.size === 0) { +- neededController.abort(getAbortReason(signal)) +- } +- }) ++ return settlementWaiters ++ .wait({ ++ signal, ++ abortInMicrotask: true, ++ createAbortError: () => getAbortReason(signal) ++ }) ++ .finally(() => { ++ waiters.delete(waiter) ++ if (!settled && waiters.size === 0) { ++ neededController.abort(getAbortReason(signal)) ++ } ++ }) + } + } + } +diff --git a/src/shared/promise-settlement-waiters.ts b/src/shared/promise-settlement-waiters.ts +index 98ec24b25c..97230d54dd 100644 +--- a/src/shared/promise-settlement-waiters.ts ++++ b/src/shared/promise-settlement-waiters.ts +@@ -13,8 +13,10 @@ type PromiseSettlementWaiter = { + + export type PromiseSettlementWaitOptions = { + signal?: AbortSignal ++ /** Preserve Promise.race ordering when raw settlement and abort share a turn. */ ++ abortInMicrotask?: boolean + timeoutMs?: number +- createAbortError?: () => Error ++ createAbortError?: () => unknown + createTimeoutError?: () => Error + onFulfilled?: (value: T) => void + onAbandon?: (reason: 'abort' | 'timeout') => void +@@ -50,7 +52,7 @@ export class PromiseSettlementWaiters { + } + return new Promise((resolve, reject) => { + let waiter!: PromiseSettlementWaiter +- const abandon = (reason: 'abort' | 'timeout', error: Error): void => { ++ const abandon = (reason: 'abort' | 'timeout', error: unknown): void => { + if (!this.waiters.delete(waiter)) { + return + } +@@ -58,8 +60,14 @@ export class PromiseSettlementWaiters { + options.onAbandon?.(reason) + reject(error) + } +- const onAbort = (): void => +- abandon('abort', options.createAbortError?.() ?? createDefaultAbortError()) ++ const onAbort = (): void => { ++ const error = options.createAbortError?.() ?? createDefaultAbortError() ++ if (options.abortInMicrotask) { ++ queueMicrotask(() => abandon('abort', error)) ++ } else { ++ abandon('abort', error) ++ } ++ } + waiter = { + resolve, + reject, diff --git a/docs/audits/auth-filesystem-wait-retention/node-results.json b/docs/audits/auth-filesystem-wait-retention/node-results.json new file mode 100644 index 00000000000..fd9974f64fb --- /dev/null +++ b/docs/audits/auth-filesystem-wait-retention/node-results.json @@ -0,0 +1,597 @@ +{ + "sourceHashes": { + "src/main/rate-limits/auth-filesystem-operation.ts": { + "before": "581d7045220e92602ca83ec335576889dcba5ada4a699e866ea1dba10afbecac", + "after": "bfac9dc25c3f3c36ab590bc6e01be39ec19dea5a872f5c409ed149e65258f9e2" + }, + "src/shared/promise-settlement-waiters.ts": { + "before": "b0b60bae6dcca4bb4f335ef7dda250f4350aa5b0306d3595b887cc4af4493060", + "after": "92618db591cf1fc1ad52f257cc9f3314310bf902fbd24e5d18abcf93afb8e722" + } + }, + "runtime": { + "node": "26.6.0", + "acorn": "8.17.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.3", + "libffi": "3.7.1", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "147", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "3.6.3", + "simdjson": "4.6.6", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2026a", + "undici": "8.9.0", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "14.6.202.34-node.26", + "zlib": "1.2.12", + "zstd": "1.5.7" + }, + "scenario": "128 aborted waits on one already-started unresolved operation; amplified arm has 64 KiB synthetic payload per abort Error; real native filesystem stall not reproduced", + "before": { + "cases": [ + { + "amplify": false, + "abortedWaits": 128, + "unresolved": { + "reasons": 1, + "controllers": 0, + "payloads": 0, + "rawCalls": 1 + }, + "settled": { + "reasons": 1, + "controllers": 0, + "payloads": 0 + }, + "dropped": { + "reasons": 0, + "controllers": 0, + "payloads": 0 + } + }, + { + "amplify": true, + "abortedWaits": 128, + "unresolved": { + "reasons": 1, + "controllers": 0, + "payloads": 1, + "rawCalls": 1 + }, + "settled": { + "reasons": 1, + "controllers": 0, + "payloads": 1 + }, + "dropped": { + "reasons": 0, + "controllers": 0, + "payloads": 0 + } + } + ], + "controls": { + "lateResultDelivered": true, + "settledResultDelivered": true, + "oneRawCall": 1, + "rawRejectionPreserved": true, + "preAbortedRawCalls": 0, + "allAbortListenersRemoved": true, + "arbitraryAbortReasonsPreserved": 4, + "liveSiblingSurvivesAbort": true + }, + "settlementOrder": [ + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 1, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 2, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 3, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 4, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 5, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 1, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 2, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 3, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 4, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 5, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 1, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 2, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 3, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 4, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 5, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 1, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 2, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 3, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 4, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 5, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + } + ], + "importedHashes": { + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + }, + "bundleSha256": "f895e71ca0f2102e7ff36fa133ae2065fa9810769d7ee6059c826f1a72bcc122" + }, + "after": { + "cases": [ + { + "amplify": false, + "abortedWaits": 128, + "unresolved": { + "reasons": 1, + "controllers": 0, + "payloads": 0, + "rawCalls": 1 + }, + "settled": { + "reasons": 1, + "controllers": 0, + "payloads": 0 + }, + "dropped": { + "reasons": 0, + "controllers": 0, + "payloads": 0 + } + }, + { + "amplify": true, + "abortedWaits": 128, + "unresolved": { + "reasons": 1, + "controllers": 0, + "payloads": 1, + "rawCalls": 1 + }, + "settled": { + "reasons": 1, + "controllers": 0, + "payloads": 1 + }, + "dropped": { + "reasons": 0, + "controllers": 0, + "payloads": 0 + } + } + ], + "controls": { + "lateResultDelivered": true, + "settledResultDelivered": true, + "oneRawCall": 1, + "rawRejectionPreserved": true, + "preAbortedRawCalls": 0, + "allAbortListenersRemoved": true, + "arbitraryAbortReasonsPreserved": 4, + "liveSiblingSurvivesAbort": true + }, + "settlementOrder": [ + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 1, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 2, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 3, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 4, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": false, + "ticks": 5, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 1, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 2, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 3, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 4, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": true, + "rejectRaw": true, + "ticks": 5, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 1, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 2, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 3, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 4, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": false, + "ticks": 5, + "outcome": { + "status": "fulfilled", + "value": "raw success" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 0, + "outcome": { + "status": "rejected", + "reason": "caller aborted" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 1, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 2, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 3, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 4, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + }, + { + "startedBefore": false, + "rejectRaw": true, + "ticks": 5, + "outcome": { + "status": "rejected", + "reason": "raw failure" + } + } + ], + "importedHashes": { + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + }, + "bundleSha256": "04324b281a776aa1018995a36d11ff7cf56b4dd737761c3d5b0fb1ebc0047652" + } +} diff --git a/docs/audits/auth-filesystem-wait-retention/reproduce.cjs b/docs/audits/auth-filesystem-wait-retention/reproduce.cjs new file mode 100644 index 00000000000..acc537f286f --- /dev/null +++ b/docs/audits/auth-filesystem-wait-retention/reproduce.cjs @@ -0,0 +1,242 @@ +const assert = require('node:assert/strict') +const { createHash } = require('node:crypto') +const { readFileSync, writeFileSync } = require('node:fs') +const { resolve } = require('node:path') +const Module = require('node:module') +const { getEventListeners } = require('node:events') +const { build } = require('esbuild') +const { root, before, after, hashes } = require('./sources.cjs')() +const settlementOrder = require('./settlement-order.cjs') + +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function') +const sourcePath = 'src/main/rate-limits/auth-filesystem-operation.ts' +const entry = resolve(root, sourcePath) +let candidate = false +let createAuthFilesystemOperation +const turn = () => new Promise((resolveTurn) => setImmediate(resolveTurn)) +async function collect() { + for (let index = 0; index < 5; index++) { + await turn() + global.gc() + } + await turn() +} +const count = (refs) => refs.reduce((total, ref) => total + Number(ref.deref() !== undefined), 0) +async function abandonedWait(operation, index, amplify) { + const controller = new AbortController() + const reason = new Error(`synthetic expired poll ${index}`) + // Payload amplifies the retained rejection object; normal timeout errors are much smaller. + if (amplify) { + reason.auditPayload = new Uint8Array(64 * 1024) + reason.auditPayload.fill(index & 255) + } + const references = { + reason: new WeakRef(reason), + controller: new WeakRef(controller), + ...(amplify ? { payload: new WeakRef(reason.auditPayload) } : {}) + } + const waiting = operation.wait(controller.signal) + controller.abort(reason) + await waiting.catch(() => {}) + assert.equal(getEventListeners(controller.signal, 'abort').length, 0) + return references +} +function snapshot(refs) { + return { + reasons: count(refs.map((ref) => ref.reason)), + controllers: count(refs.map((ref) => ref.controller)), + payloads: count(refs.flatMap((ref) => (ref.payload ? [ref.payload] : []))) + } +} +async function retention(amplify) { + let settleRaw + let rawCalls = 0 + let operation = createAuthFilesystemOperation('/synthetic-local-auth', () => { + rawCalls++ + return new Promise((resolveRaw) => { + settleRaw = resolveRaw + }) + }) + await turn() + assert.equal(rawCalls, 1) + const refs = [] + for (let index = 0; index < 128; index++) { + refs.push(await abandonedWait(operation, index, amplify)) + } + await collect() + const unresolved = { ...snapshot(refs), rawCalls } + if (candidate) { + assert.equal(unresolved.reasons, 1) + } + settleRaw('finished') + await operation.result + await collect() + const settled = snapshot(refs) + assert.equal(settled.reasons, 1) + operation = null + settleRaw = null + await collect() + const dropped = snapshot(refs) + assert.deepEqual(dropped, { reasons: 0, controllers: 0, payloads: 0 }) + return { amplify, abortedWaits: refs.length, unresolved, settled, dropped } +} +async function controls() { + let rawCalls = 0 + let finish + const operation = createAuthFilesystemOperation('/synthetic-auth-controls', () => { + rawCalls++ + return new Promise((resolveRaw) => { + finish = resolveRaw + }) + }) + const expired = new AbortController() + const expiredReason = new Error('expired first poll') + const abortedWait = operation.wait(expired.signal) + await turn() + expired.abort(expiredReason) + await assert.rejects(abortedWait, (reason) => reason === expiredReason) + const later = new AbortController() + const lateWait = operation.wait(later.signal) + finish('late raw result') + assert.equal(await lateWait, 'late raw result') + assert.equal(rawCalls, 1) + assert.equal(await operation.wait(later.signal), 'late raw result') + assert.equal(getEventListeners(expired.signal, 'abort').length, 0) + assert.equal(getEventListeners(later.signal, 'abort').length, 0) + let rejectedCalls = 0 + const rejectedReason = new Error('raw rejected') + const rejected = createAuthFilesystemOperation('/synthetic-auth-rejected', async () => { + rejectedCalls++ + throw rejectedReason + }) + await assert.rejects( + rejected.wait(new AbortController().signal), + (reason) => reason === rejectedReason + ) + let preAbortedCalls = 0 + const preAborted = createAuthFilesystemOperation('/synthetic-auth-pre-aborted', async () => { + preAbortedCalls++ + return 'unexpected' + }) + const priorAbort = new AbortController() + priorAbort.abort(expiredReason) + await assert.rejects(preAborted.wait(priorAbort.signal), (reason) => reason === expiredReason) + await assert.rejects(preAborted.result, (reason) => reason === expiredReason) + assert.equal(preAbortedCalls, 0) + let finishReasons + const reasonOperation = createAuthFilesystemOperation( + '/synthetic-auth-reasons', + () => + new Promise((resolveRaw) => { + finishReasons = resolveRaw + }) + ) + await turn() + const reasons = [false, 0, 'string abort', { code: 'custom' }] + for (const reason of reasons) { + const controller = new AbortController() + const pending = reasonOperation.wait(controller.signal) + controller.abort(reason) + await assert.rejects(pending, (observed) => observed === reason) + assert.equal(getEventListeners(controller.signal, 'abort').length, 0) + } + const activeController = new AbortController() + const cancelledController = new AbortController() + const active = reasonOperation.wait(activeController.signal) + const cancelled = reasonOperation.wait(cancelledController.signal) + cancelledController.abort(expiredReason) + await assert.rejects(cancelled, (reason) => reason === expiredReason) + finishReasons('active result') + assert.equal(await active, 'active result') + assert.equal(getEventListeners(activeController.signal, 'abort').length, 0) + return { + lateResultDelivered: true, + settledResultDelivered: true, + oneRawCall: rawCalls, + rawRejectionPreserved: rejectedCalls === 1, + preAbortedRawCalls: preAbortedCalls, + allAbortListenersRemoved: true, + arbitraryAbortReasonsPreserved: reasons.length, + liveSiblingSurvivesAbort: true + } +} +async function phase(sources, fixed) { + candidate = fixed + const built = await build({ + entryPoints: [entry], + bundle: true, + platform: 'node', + format: 'cjs', + write: false, + metafile: true, + logLevel: 'silent', + plugins: [ + { + name: 'hash-fenced-proof-source', + setup(api) { + api.onLoad( + { filter: /(?:auth-filesystem-operation|promise-settlement-waiters)\.ts$/ }, + (args) => + sources.has(args.path) + ? { + contents: sources.get(args.path), + loader: 'ts', + resolveDir: resolve(args.path, '..') + } + : undefined + ) + } + } + ] + }) + const bundled = built.outputFiles[0].text + const moduleOwner = new Module(entry, module) + moduleOwner.filename = entry + moduleOwner.paths = module.paths + moduleOwner._compile(bundled, entry) + createAuthFilesystemOperation = moduleOwner.exports.createAuthFilesystemOperation + const importedHashes = Object.fromEntries( + Object.keys(built.metafile.inputs) + .filter((path) => !sources.has(resolve(root, path))) + .map((path) => [ + path, + createHash('sha256') + .update(readFileSync(resolve(root, path))) + .digest('hex') + ]) + ) + return { + cases: [await retention(false), await retention(true)], + controls: await controls(), + settlementOrder: await settlementOrder(createAuthFilesystemOperation), + importedHashes, + bundleSha256: createHash('sha256').update(bundled).digest('hex') + } +} +async function run() { + const result = { + sourceHashes: hashes, + runtime: process.versions, + scenario: + '128 aborted waits on one already-started unresolved operation; amplified arm has 64 KiB synthetic payload per abort Error; real native filesystem stall not reproduced', + before: await phase(before, false), + after: await phase(after, true) + } + assert.deepEqual(result.after.settlementOrder, result.before.settlementOrder) + const output = process.argv[2] + ? resolve(process.argv[2]) + : resolve(__dirname, `${process.versions.electron ? 'electron-' : 'node-'}results.json`) + writeFileSync(output, `${JSON.stringify(result, null, 2)}\n`) + console.log(JSON.stringify(result, null, 2)) +} +const deadline = setTimeout(() => { + console.error('Auth wait proof exceeded 15 seconds') + process.exit(1) +}, 15_000) +run() + .catch((error) => { + console.error(error) + process.exitCode = 1 + }) + .finally(() => clearTimeout(deadline)) diff --git a/docs/audits/auth-filesystem-wait-retention/settlement-order.cjs b/docs/audits/auth-filesystem-wait-retention/settlement-order.cjs new file mode 100644 index 00000000000..2a830f7042a --- /dev/null +++ b/docs/audits/auth-filesystem-wait-retention/settlement-order.cjs @@ -0,0 +1,37 @@ +const turn = () => new Promise((resolveTurn) => setImmediate(resolveTurn)) + +module.exports = async function settlementOrder(create) { + const cases = [] + for (const startedBefore of [true, false]) { + for (const rejectRaw of [false, true]) { + for (let ticks = 0; ticks < 6; ticks++) { + let settle + const operation = create( + 'synthetic-auth-order', + () => + new Promise((resolveRaw, failRaw) => { + settle = () => (rejectRaw ? failRaw('raw failure') : resolveRaw('raw success')) + }) + ) + await turn() + const controller = new AbortController() + const start = () => + operation.wait(controller.signal).then( + (value) => ({ status: 'fulfilled', value }), + (reason) => ({ status: 'rejected', reason }) + ) + let waiting = startedBefore ? start() : null + settle() + for (let index = 0; index < ticks; index++) { + await Promise.resolve() + } + if (!startedBefore) { + waiting = start() + } + controller.abort('caller aborted') + cases.push({ startedBefore, rejectRaw, ticks, outcome: await waiting }) + } + } + } + return cases +} diff --git a/docs/audits/auth-filesystem-wait-retention/source-versions.json b/docs/audits/auth-filesystem-wait-retention/source-versions.json new file mode 100644 index 00000000000..c548dfe05f5 --- /dev/null +++ b/docs/audits/auth-filesystem-wait-retention/source-versions.json @@ -0,0 +1,11 @@ +{ + "baselineHashes": { + "src/main/rate-limits/auth-filesystem-operation.ts": "581d7045220e92602ca83ec335576889dcba5ada4a699e866ea1dba10afbecac", + "src/shared/promise-settlement-waiters.ts": "b0b60bae6dcca4bb4f335ef7dda250f4350aa5b0306d3595b887cc4af4493060" + }, + "checkedMainRevision": "77cd61df396f25ec91ee2d5ddcbd1f55aa94f818", + "sharedTestBaselineSha256": "1e274da93106f31b9b57aa48fd965a4d81c8adf4ebe1bfa20a196aa3e529a73a", + "authSourceIdenticalNamedRefs": ["origin/main", "v1.4.198"], + "registrySourceIdenticalNamedRefs": ["origin/main"], + "historicalRuntimeReproduced": false +} diff --git a/docs/audits/auth-filesystem-wait-retention/sources.cjs b/docs/audits/auth-filesystem-wait-retention/sources.cjs new file mode 100644 index 00000000000..94a8c1da504 --- /dev/null +++ b/docs/audits/auth-filesystem-wait-retention/sources.cjs @@ -0,0 +1,29 @@ +const assert = require('node:assert/strict') +const { createHash } = require('node:crypto') +const { readFileSync } = require('node:fs') +const { resolve } = require('node:path') +const { applyPatch, parsePatch, reversePatch } = require('diff') + +module.exports = function loadSources() { + const root = resolve(__dirname, '../../..') + const expected = JSON.parse(readFileSync(resolve(__dirname, 'source-versions.json'), 'utf8')) + const parsed = parsePatch(readFileSync(resolve(__dirname, 'fix.patch'), 'utf8')) + const before = new Map() + const after = new Map() + const hashes = {} + assert.equal(parsed.length, 2) + for (const patch of parsed) { + const path = patch.newFileName.replace(/^b\//, '') + assert.ok(Object.hasOwn(expected.baselineHashes, path), `Unexpected patch path: ${path}`) + const absolute = resolve(root, path) + const current = readFileSync(absolute, 'utf8') + const baseline = applyPatch(current, reversePatch(patch)) + assert.notEqual(baseline, false, `Source changed; review fix.patch: ${path}`) + const hash = (source) => createHash('sha256').update(source).digest('hex') + assert.equal(hash(baseline), expected.baselineHashes[path], `Baseline drift: ${path}`) + before.set(absolute, baseline) + after.set(absolute, current) + hashes[path] = { before: hash(baseline), after: hash(current) } + } + return { root, before, after, hashes } +} diff --git a/docs/audits/auth-filesystem-wait-retention/validation.json b/docs/audits/auth-filesystem-wait-retention/validation.json new file mode 100644 index 00000000000..d9732979b1e --- /dev/null +++ b/docs/audits/auth-filesystem-wait-retention/validation.json @@ -0,0 +1,61 @@ +{ + "backgroundLaunch": true, + "authAndRegistryTests": { + "before": { "passed": 48, "failed": 2, "exitCode": 1 }, + "after": { "passed": 50, "failed": 0, "exitCode": 0 }, + "newCases": 18, + "expectedBeforeFailures": [ + "PromiseSettlementWaiters preserves abort scheduling with abortInMicrotask=true", + "shared auth filesystem wait lifetime releases aborted poll reasons while one native operation remains needed" + ], + "paths": [ + "src/main/rate-limits/auth-filesystem-operation.test.ts", + "src/main/rate-limits/auth-filesystem-operation-retention.test.ts", + "src/main/rate-limits/codex-auth-presence.test.ts", + "src/main/rate-limits/kimi-fetcher-wsl-home.test.ts", + "src/main/rate-limits/kimi-fetcher.test.ts", + "src/shared/promise-settlement-waiters.test.ts" + ] + }, + "existingRegistryConsumers": { + "passed": 39, + "failed": 0, + "exitCode": 0, + "paths": [ + "src/relay/relay-watcher-setup-wait.test.ts", + "src/relay/relay-filesystem-watch-registry.test.ts", + "src/main/providers/ssh-filesystem-provider-watch-waiters.test.ts", + "src/main/runtime/file-watcher-host.test.ts", + "src/main/ipc/runtime-watcher-pending-assignment.test.ts", + "src/main/ipc/parcel-watcher-supervisor-capacity-wait.test.ts" + ] + }, + "typechecks": { + "command": "node config/scripts/run-typecheck-projects-in-parallel.mjs", + "projects": [ + "config/tsconfig.node.json", + "config/tsconfig.tc.cli.json", + "config/tsconfig.tc.web.json" + ], + "exitCode": 0 + }, + "focusedOxlint": { "ordinaryExitCode": 0, "typeAwareExitCode": 0 }, + "formatCheckExitCode": 0, + "changedCodeQuality": { + "base": "2fccacadbe23", + "changedFiles": 297, + "newFindings": 0, + "exitCode": 0 + }, + "proof": { + "node": "26.6.0", + "electron": "43.7.0", + "electronNode": "24.21.0", + "bothExitCode": 0, + "orderingCasesPerRuntime": 24, + "beforeAfterOrderingEqual": true, + "rawFilesystemStall": "injected pending promise, not an affected-host capture", + "amplifiedBytesPerCase": 8388608, + "ordinaryErrorBytes": "not measured" + } +} diff --git a/src/main/rate-limits/auth-filesystem-operation-retention.test.ts b/src/main/rate-limits/auth-filesystem-operation-retention.test.ts new file mode 100644 index 00000000000..a79cfd63af9 --- /dev/null +++ b/src/main/rate-limits/auth-filesystem-operation-retention.test.ts @@ -0,0 +1,166 @@ +import { getEventListeners } from 'node:events' +import { describe, expect, it, vi } from 'vitest' +import { + createAuthFilesystemOperation, + type SharedAuthFilesystemOperation +} from './auth-filesystem-operation' + +function pendingOperation(): { + operation: SharedAuthFilesystemOperation + resolve: (value: string) => void + reject: (reason: unknown) => void + rawCalls: () => number +} { + let resolve = (_value: string): void => {} + let reject = (_reason: unknown): void => {} + let calls = 0 + const operation = createAuthFilesystemOperation('auth-retention-fixture', () => { + calls += 1 + return new Promise((resolveRaw, rejectRaw) => { + resolve = resolveRaw + reject = rejectRaw + }) + }) + return { + operation, + resolve: (value) => resolve(value), + reject: (reason) => reject(reason), + rawCalls: () => calls + } +} + +async function abortWait( + operation: SharedAuthFilesystemOperation +): Promise> { + const controller = new AbortController() + const reason = new Error('Auth poll expired') + const weakReason = new WeakRef(reason) + const result = operation.wait(controller.signal) + controller.abort(reason) + await result.catch((error: unknown) => { + if (error !== reason) { + throw new Error('Abort reason identity changed') + } + }) + expect(getEventListeners(controller.signal, 'abort')).toHaveLength(0) + return weakReason +} + +async function collect(): Promise { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + for (let round = 0; round < 5; round += 1) { + await new Promise((resolve) => setImmediate(resolve)) + globalThis.gc() + } +} + +describe('shared auth filesystem wait lifetime', () => { + it.each( + [true, false].flatMap((startedBefore) => + [true, false].flatMap((rejectRaw) => + [0, 1].map((ticks) => ({ startedBefore, rejectRaw, ticks })) + ) + ) + )('preserves raw-result/abort ordering for %j', async ({ startedBefore, rejectRaw, ticks }) => { + const pending = pendingOperation() + const controller = new AbortController() + await new Promise((resolve) => setImmediate(resolve)) + const start = (): Promise => + pending.operation.wait(controller.signal).then( + (value) => ({ status: 'fulfilled', value }), + (reason: unknown) => ({ status: 'rejected', reason }) + ) + let waiting = startedBefore ? start() : undefined + if (rejectRaw) { + pending.reject('raw failure') + } else { + pending.resolve('raw success') + } + for (let tick = 0; tick < ticks; tick += 1) { + await Promise.resolve() + } + waiting ??= start() + controller.abort('caller aborted') + expect(await waiting).toEqual( + ticks === 0 + ? { status: 'rejected', reason: 'caller aborted' } + : rejectRaw + ? { status: 'rejected', reason: 'raw failure' } + : { status: 'fulfilled', value: 'raw success' } + ) + expect(pending.rawCalls()).toBe(1) + expect(getEventListeners(controller.signal, 'abort')).toHaveLength(0) + }) + + it('releases aborted poll reasons while one native operation remains needed', async () => { + const pending = pendingOperation() + const anchorController = new AbortController() + const anchor = pending.operation.wait(anchorController.signal) + await Promise.resolve() + const thenSpy = vi.spyOn(pending.operation.result, 'then') + try { + const reasons: WeakRef[] = [] + for (let index = 0; index < 64; index += 1) { + reasons.push(await abortWait(pending.operation)) + } + await collect() + expect(reasons.filter((ref) => ref.deref() !== undefined)).toHaveLength(0) + expect(pending.rawCalls()).toBe(1) + // Each native-result reaction would outlive every abandoned poll. + expect(thenSpy).not.toHaveBeenCalled() + } finally { + thenSpy.mockRestore() + pending.resolve('finished') + await anchor + } + expect(getEventListeners(anchorController.signal, 'abort')).toHaveLength(0) + }) + + it('serves a late and then settled result after all previous polls abort', async () => { + const pending = pendingOperation() + await Promise.resolve() + await abortWait(pending.operation) + const controller = new AbortController() + const late = pending.operation.wait(controller.signal) + pending.resolve('late result') + await expect(late).resolves.toBe('late result') + await expect(pending.operation.wait(controller.signal)).resolves.toBe('late result') + expect(pending.rawCalls()).toBe(1) + expect(getEventListeners(controller.signal, 'abort')).toHaveLength(0) + }) + + it('preserves a live sibling and forwards raw failure identity to later waits', async () => { + const pending = pendingOperation() + const controller = new AbortController() + const live = pending.operation.wait(controller.signal) + await Promise.resolve() + await abortWait(pending.operation) + const reason = new Error('Raw filesystem failure') + const rejected = expect(live).rejects.toBe(reason) + pending.reject(reason) + await rejected + await expect(pending.operation.wait(controller.signal)).rejects.toBe(reason) + expect(pending.rawCalls()).toBe(1) + expect(getEventListeners(controller.signal, 'abort')).toHaveLength(0) + }) + + it.each([false, 0, 'custom abort', { code: 'custom abort' }])( + 'preserves the arbitrary abort reason %j', + async (reason) => { + const pending = pendingOperation() + const controller = new AbortController() + await Promise.resolve() + const wait = pending.operation.wait(controller.signal) + controller.abort(reason) + try { + await expect(wait).rejects.toBe(reason) + expect(getEventListeners(controller.signal, 'abort')).toHaveLength(0) + } finally { + pending.resolve('finished') + await pending.operation.result + } + } + ) +}) diff --git a/src/main/rate-limits/auth-filesystem-operation.ts b/src/main/rate-limits/auth-filesystem-operation.ts index 228234e92c0..7d030ab01e3 100644 --- a/src/main/rate-limits/auth-filesystem-operation.ts +++ b/src/main/rate-limits/auth-filesystem-operation.ts @@ -1,4 +1,5 @@ import { parseWslUncPath } from '../../shared/wsl-paths' +import { PromiseSettlementWaiters } from '../../shared/promise-settlement-waiters' const MAX_CONCURRENT_WSL_AUTH_OPERATIONS = 2 const activeWslOperationDistros = new Set() @@ -139,10 +140,9 @@ export function createAuthFilesystemOperation( const waiters = new Set() let settled = false const result = scheduleAuthFilesystemOperation(authPath, neededController.signal, operation) - const markSettled = (): void => { + const settlementWaiters = new PromiseSettlementWaiters(result, () => { settled = true - } - void result.then(markSettled, markSettled) + }) return { result, @@ -156,20 +156,18 @@ export function createAuthFilesystemOperation( const waiter = Symbol('auth-filesystem-waiter') waiters.add(waiter) - let onAbort: (() => void) | null = null - const aborted = new Promise((_resolve, reject) => { - onAbort = () => reject(getAbortReason(signal)) - signal.addEventListener('abort', onAbort, { once: true }) - }) - return Promise.race([result, aborted]).finally(() => { - if (onAbort) { - signal.removeEventListener('abort', onAbort) - } - waiters.delete(waiter) - if (!settled && waiters.size === 0) { - neededController.abort(getAbortReason(signal)) - } - }) + return settlementWaiters + .wait({ + signal, + abortInMicrotask: true, + createAbortError: () => getAbortReason(signal) + }) + .finally(() => { + waiters.delete(waiter) + if (!settled && waiters.size === 0) { + neededController.abort(getAbortReason(signal)) + } + }) } } } diff --git a/src/shared/promise-settlement-waiters.test.ts b/src/shared/promise-settlement-waiters.test.ts index dfb9b50b7bb..0af1c1c61ef 100644 --- a/src/shared/promise-settlement-waiters.test.ts +++ b/src/shared/promise-settlement-waiters.test.ts @@ -2,6 +2,43 @@ import { describe, expect, it, vi } from 'vitest' import { PromiseSettlementWaiters } from './promise-settlement-waiters' describe('PromiseSettlementWaiters', () => { + it.each([false, true])('preserves abort scheduling with abortInMicrotask=%s', async (defer) => { + let resolveBase = (_value: string): void => {} + const base = new Promise((resolve) => { + resolveBase = resolve + }) + const waiters = new PromiseSettlementWaiters(base) + const controller = new AbortController() + const reason = { code: 'aborted' } + const wait = waiters.wait({ + signal: controller.signal, + abortInMicrotask: defer, + createAbortError: () => reason + }) + resolveBase('raw result') + controller.abort() + await (defer ? expect(wait).resolves.toBe('raw result') : expect(wait).rejects.toBe(reason)) + expect(waiters.waiterCount).toBe(0) + }) + + it('lets an earlier deferred abort win over a later raw settlement', async () => { + let resolveBase = (_value: string): void => {} + const base = new Promise((resolve) => { + resolveBase = resolve + }) + const waiters = new PromiseSettlementWaiters(base) + const controller = new AbortController() + const wait = waiters.wait({ + signal: controller.signal, + abortInMicrotask: true, + createAbortError: () => false + }) + controller.abort() + resolveBase('raw result') + await expect(wait).rejects.toBe(false) + expect(waiters.waiterCount).toBe(0) + }) + it('removes ten thousand aborted callers while one anchor remains pending', async () => { let resolveBase: (value: number) => void = () => {} const basePromise = new Promise((resolve) => { diff --git a/src/shared/promise-settlement-waiters.ts b/src/shared/promise-settlement-waiters.ts index 98ec24b25c1..97230d54dd6 100644 --- a/src/shared/promise-settlement-waiters.ts +++ b/src/shared/promise-settlement-waiters.ts @@ -13,8 +13,10 @@ type PromiseSettlementWaiter = { export type PromiseSettlementWaitOptions = { signal?: AbortSignal + /** Preserve Promise.race ordering when raw settlement and abort share a turn. */ + abortInMicrotask?: boolean timeoutMs?: number - createAbortError?: () => Error + createAbortError?: () => unknown createTimeoutError?: () => Error onFulfilled?: (value: T) => void onAbandon?: (reason: 'abort' | 'timeout') => void @@ -50,7 +52,7 @@ export class PromiseSettlementWaiters { } return new Promise((resolve, reject) => { let waiter!: PromiseSettlementWaiter - const abandon = (reason: 'abort' | 'timeout', error: Error): void => { + const abandon = (reason: 'abort' | 'timeout', error: unknown): void => { if (!this.waiters.delete(waiter)) { return } @@ -58,8 +60,14 @@ export class PromiseSettlementWaiters { options.onAbandon?.(reason) reject(error) } - const onAbort = (): void => - abandon('abort', options.createAbortError?.() ?? createDefaultAbortError()) + const onAbort = (): void => { + const error = options.createAbortError?.() ?? createDefaultAbortError() + if (options.abortInMicrotask) { + queueMicrotask(() => abandon('abort', error)) + } else { + abandon('abort', error) + } + } waiter = { resolve, reject, From 51f809aa82b58343a38cdba1920190bfd01da3a0 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:12:08 -0700 Subject: [PATCH 052/168] fix: retire obsolete GitLab host cache generations (#21136) Co-authored-by: m4air --- .../gitlab-known-host-retirement/README.md | 39 +++ .../electron-results.json | 89 ++++++ .../gitlab-known-host-retirement/fix.patch | 103 +++++++ .../original-source-hashes.json | 3 + .../reproduce.cjs | 257 ++++++++++++++++++ .../gitlab-known-host-retirement/results.json | 88 ++++++ .../gitlab-known-host-retirement/sources.cjs | 30 ++ src/main/gitlab/gitlab-known-host-probe.ts | 44 ++- .../gitlab-known-host-retirement.test.ts | 171 ++++++++++++ 9 files changed, 813 insertions(+), 11 deletions(-) create mode 100644 docs/audits/gitlab-known-host-retirement/README.md create mode 100644 docs/audits/gitlab-known-host-retirement/electron-results.json create mode 100644 docs/audits/gitlab-known-host-retirement/fix.patch create mode 100644 docs/audits/gitlab-known-host-retirement/original-source-hashes.json create mode 100644 docs/audits/gitlab-known-host-retirement/reproduce.cjs create mode 100644 docs/audits/gitlab-known-host-retirement/results.json create mode 100644 docs/audits/gitlab-known-host-retirement/sources.cjs create mode 100644 src/main/gitlab/gitlab-known-host-retirement.test.ts diff --git a/docs/audits/gitlab-known-host-retirement/README.md b/docs/audits/gitlab-known-host-retirement/README.md new file mode 100644 index 00000000000..249a7ad8fbc --- /dev/null +++ b/docs/audits/gitlab-known-host-retirement/README.md @@ -0,0 +1,39 @@ +# Retire obsolete GitLab known-host generations + +Each successful `getGlabKnownHosts` probe previously stored a host-list array under a connection ID plus its SSH provider generation. Reconnecting under the same ID created a new entry while every earlier successful generation remained cached until an explicit preflight reset. The cache now keeps one successful generation per observed execution identity. + +Async publication also uses the existing coalescer's `ownsKey()` and checks the current SSH generation. A result completing after reconnect, explicit reset, or replacement by a newer probe cannot recreate retired cache state. Original callers can still receive their own completed result. Explicitly remembered hosts, native/WSL separation, command routing and existing probe timeouts are preserved. + +## Evidence + +The runner bundles the actual cache, coalescer and parser. Only command-result and SSH-generation ports are controlled; it opens no SSH connection and runs no GitLab command. It reverses `fix.patch` in memory, verifies the original source hash, and compares that baseline against the unmodified current product source. Reports include product, dependency, regression-test and fixture hashes. + +| Control | Original | Fixed | +| --- | --- | --- | +| Successful result arrays retained after 128 generations | 128 | 1 current array | +| Remembered result arrays retained after 16 generations | 16 | 1 current array | +| Delayed old-generation result after a successor answers | Still retained | Collectable; successor preserved | +| Explicit reset followed by old completion | Old result repopulates cache | Next lookup executes a fresh probe | +| Abandoned probe finishes after its replacement | Old host added to replacement cache | Replacement remains unchanged | +| Explicit reset after retention exercise | 0 original arrays retained | 0 original arrays retained | + +Both phases preserve remembered-host updates while probes succeed or fail and isolate native, Ubuntu WSL, Debian WSL and two connection IDs. `results.json` records Node26.6; `electron-results.json` records installed Electron43.7 / Node24.21 running without an app window. Both runs pass all controls. This is compatibility evidence, not a historical packaged-binary reproduction. + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc docs/audits/gitlab-known-host-retirement/reproduce.cjs +ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/gitlab/gitlab-known-host-retirement.test.ts src/main/gitlab/gitlab-known-host-probe.test.ts src/main/gitlab/gitlab-known-host-probe-wsl-fallback.test.ts src/main/git/coalesced-probe.test.ts src/main/gitlab/client-mr-auth-rate-limit.test.ts +``` + +For the installed macOS Electron binary: + +```sh +ELECTRON_RUN_AS_NODE=1 ORCA_BACKGROUND_LAUNCH=1 node_modules/electron/dist/Electron.app/Contents/MacOS/Electron --expose-gc docs/audits/gitlab-known-host-retirement/reproduce.cjs docs/audits/gitlab-known-host-retirement/electron-results.json +``` + +The runner is portable; that executable path is macOS-specific. Thirty-two focused tests pass, including eight new retention/lifecycle/scope controls. Running those eight against the original source produces six failures and two passing controls. Node typecheck, focused lint (including artifact type-aware/casting scans), and the changed-code quality gate pass. The original product module and reused coalescer match named main `291b4ddd6f1c1af480169885e0fda7f9c78ff053`. + +## Limits and incident mapping + +This removes small metadata retained across SSH generations. Distinct historical execution identities may still keep one entry each until reset; this change does not impose a new cache cap or alter connection/provider lifetime. One generation's host list remains input-sized. + +The demonstrated accumulation requires changing SSH provider generations, so it cannot explain [#19831](https://github.com/stablyai/orca/issues/19831)'s reported all-local session. No affected-host observation ties it to another OOM report. The proof measures reachable result arrays, not RSS or gigabytes of incident memory. diff --git a/docs/audits/gitlab-known-host-retirement/electron-results.json b/docs/audits/gitlab-known-host-retirement/electron-results.json new file mode 100644 index 00000000000..8dda9c71946 --- /dev/null +++ b/docs/audits/gitlab-known-host-retirement/electron-results.json @@ -0,0 +1,89 @@ +{ + "sourceHashes": { + "src/main/gitlab/gitlab-known-host-probe.ts": { + "baseline": "4f9651c5a383438aa968aec082c200a3371e2f4317372125aa11aa6938793c25", + "fixed": "ecf0d67f68cf4b7b1bc7d6ff19a1815a8b95fc9721962d3cd873702f98831bad" + }, + "src/main/git/coalesced-probe.ts": "e5a13820a7d8b5f501a3804961ea26526bd6ad9144ecf597d5d83a70d81885e1", + "src/main/git/remote-ref-probe-cache.ts": "d5cbfd97b30e72b03c0d28d8b9e75a2ae1f9e3efc9f5b5570c78e0742c0582dd", + "src/main/gitlab/project-ref-parser.ts": "0f8b6758e6a162a58f436ca4213addc42bf6ceb3fcc2e63e8c7402c844af9303", + "src/main/gitlab/gitlab-known-host-retirement.test.ts": "c6dedbac0462cae22903805d0a89be397a2b9d122a29640b6a07a2d274f8e98e" + }, + "proofHashes": { + "reproduce.cjs": "19f49406685923b050a99fbc92b02b9f6f5f8d8f308dcc14336415a0988679cc", + "sources.cjs": "2ee8ac0f295d65f16489da2b0c03800b9db4e88c83ecfb63ae7be1ec3ea6c148", + "fix.patch": "695c929087006b3406ce2c30a7ab783d88af3f9675f4ea320dc7f98b8476be9f", + "original-source-hashes.json": "f9770d9f0a87afc9231eef6af99e8ded33348fdc21ba46a70b1d5d3388874b97" + }, + "runtime": { + "node": "24.21.0", + "acorn": "8.18.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.2", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "148", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "0.0.0", + "simdjson": "4.6.7", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2025c", + "undici": "7.29.1", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "15.0.245.31-electron.0", + "zlib": "1.3.2.1-motley", + "zstd": "1.6.0", + "electron": "43.7.0", + "chrome": "150.0.7871.250" + }, + "phases": { + "baseline": { + "retained": { + "retained": 128, + "afterReset": 0 + }, + "rememberedGenerationsRetained": 16, + "oldGeneration": { + "oldResultRetained": true, + "replacementHostsPreserved": true + }, + "reset": { + "hosts": ["gitlab.com", "old-before-reset.test"], + "calls": 1 + }, + "abandoned": ["gitlab.com", "replacement.test", "abandoned.test"], + "rememberedSuccessAndFailure": "passed", + "nativeWslConnectionIsolation": "passed" + }, + "fixed": { + "retained": { + "retained": 1, + "afterReset": 0 + }, + "rememberedGenerationsRetained": 1, + "oldGeneration": { + "oldResultRetained": false, + "replacementHostsPreserved": true + }, + "reset": { + "hosts": ["gitlab.com", "fresh-after-reset.test"], + "calls": 2 + }, + "abandoned": ["gitlab.com", "replacement.test"], + "rememberedSuccessAndFailure": "passed", + "nativeWslConnectionIsolation": "passed" + } + } +} diff --git a/docs/audits/gitlab-known-host-retirement/fix.patch b/docs/audits/gitlab-known-host-retirement/fix.patch new file mode 100644 index 00000000000..61fb557d1d3 --- /dev/null +++ b/docs/audits/gitlab-known-host-retirement/fix.patch @@ -0,0 +1,103 @@ +diff --git a/src/main/gitlab/gitlab-known-host-probe.ts b/src/main/gitlab/gitlab-known-host-probe.ts +index 752e1b0291..d328cd38ca 100644 +--- a/src/main/gitlab/gitlab-known-host-probe.ts ++++ b/src/main/gitlab/gitlab-known-host-probe.ts +@@ -12,7 +12,10 @@ export type LocalGitExecOptions = { + + const GLAB_KNOWN_HOSTS_TIMEOUT_MS = 10_000 + const UNAUTHENTICATED_HOSTS_MAX_ENTRIES = 128 +-const knownHostsCacheByExecutionContext = new Map() ++const knownHostsCacheByExecutionContext = new Map< ++ string, ++ { key: string; hosts: readonly string[] } ++>() + const knownHostsInFlightByExecutionContext: CoalescedProbes = new Map() + const unauthenticatedHostExpiries = new Map() + +@@ -27,6 +30,19 @@ function knownHostsExecutionKey( + return localGitOptions.wslDistro ? `wsl:${localGitOptions.wslDistro}` : 'native' + } + ++function knownHostsCacheContext( ++ connectionId?: string | null, ++ localGitOptions: LocalGitExecOptions = {} ++): { key: string; cacheKey: string } { ++ const key = knownHostsExecutionKey(connectionId, localGitOptions) ++ const cacheKey = connectionId ? `connection:${connectionId}` : key ++ const cached = knownHostsCacheByExecutionContext.get(cacheKey) ++ if (cached && cached.key !== key) { ++ knownHostsCacheByExecutionContext.delete(cacheKey) ++ } ++ return { key, cacheKey } ++} ++ + /** @internal - exposed for tests only */ + export function _resetKnownHostsCache(): void { + knownHostsCacheByExecutionContext.clear() +@@ -103,8 +119,8 @@ export function rememberGlabKnownHosts( + connectionId?: string | null, + localGitOptions: LocalGitExecOptions = {} + ): void { +- const key = knownHostsExecutionKey(connectionId, localGitOptions) +- const cached = knownHostsCacheByExecutionContext.get(key) ?? DEFAULT_GITLAB_HOSTS ++ const { key, cacheKey } = knownHostsCacheContext(connectionId, localGitOptions) ++ const cached = knownHostsCacheByExecutionContext.get(cacheKey)?.hosts ?? DEFAULT_GITLAB_HOSTS + const seen = new Set(cached.map(normalizeGitLabHost)) + const additions: string[] = [] + for (const host of hosts) { +@@ -121,27 +137,29 @@ export function rememberGlabKnownHosts( + if (additions.length === 0) { + return + } +- knownHostsCacheByExecutionContext.set(key, [...cached, ...additions]) ++ knownHostsCacheByExecutionContext.set(cacheKey, { key, hosts: [...cached, ...additions] }) + } + + export async function getGlabKnownHosts( + connectionId?: string | null, + localGitOptions: LocalGitExecOptions = {} + ): Promise { +- const key = knownHostsExecutionKey(connectionId, localGitOptions) +- const cached = knownHostsCacheByExecutionContext.get(key) ++ const { key, cacheKey } = knownHostsCacheContext(connectionId, localGitOptions) ++ const cached = knownHostsCacheByExecutionContext.get(cacheKey)?.hosts + if (cached) { + return cached + } + // Why: only join a probe still young enough to answer, so a wedged one cannot + // pin every later retry for the life of the process (P1-D). +- return runCoalescedProbe(knownHostsInFlightByExecutionContext, key, () => +- probeGlabKnownHosts(key, connectionId, localGitOptions) ++ return runCoalescedProbe(knownHostsInFlightByExecutionContext, key, (ownsKey) => ++ probeGlabKnownHosts(key, cacheKey, ownsKey, connectionId, localGitOptions) + ) + } + + async function probeGlabKnownHosts( + key: string, ++ cacheKey: string, ++ ownsKey: () => boolean, + connectionId?: string | null, + localGitOptions: LocalGitExecOptions = {} + ): Promise { +@@ -160,13 +178,17 @@ async function probeGlabKnownHosts( + ...(localGitOptions.admissionTier ? { admissionTier: localGitOptions.admissionTier } : {}) + }) + const hosts = parseGlabAuthStatusHosts(`${stdout}\n${stderr}`) +- const remembered = knownHostsCacheByExecutionContext.get(key) ?? [] ++ const cached = knownHostsCacheByExecutionContext.get(cacheKey) ++ const remembered = cached?.key === key ? cached.hosts : [] + const merged = Array.from(new Set([...DEFAULT_GITLAB_HOSTS, ...remembered, ...hosts])) +- knownHostsCacheByExecutionContext.set(key, merged) ++ if (ownsKey() && knownHostsExecutionKey(connectionId, localGitOptions) === key) { ++ knownHostsCacheByExecutionContext.set(cacheKey, { key, hosts: merged }) ++ } + return merged + } catch { + // Keep failures uncached so auth or tunnel recovery is discovered later. +- return knownHostsCacheByExecutionContext.get(key) ?? [...DEFAULT_GITLAB_HOSTS] ++ const cached = knownHostsCacheByExecutionContext.get(cacheKey) ++ return cached?.key === key ? cached.hosts : [...DEFAULT_GITLAB_HOSTS] + } + } + diff --git a/docs/audits/gitlab-known-host-retirement/original-source-hashes.json b/docs/audits/gitlab-known-host-retirement/original-source-hashes.json new file mode 100644 index 00000000000..c7b307638fe --- /dev/null +++ b/docs/audits/gitlab-known-host-retirement/original-source-hashes.json @@ -0,0 +1,3 @@ +{ + "src/main/gitlab/gitlab-known-host-probe.ts": "4f9651c5a383438aa968aec082c200a3371e2f4317372125aa11aa6938793c25" +} diff --git a/docs/audits/gitlab-known-host-retirement/reproduce.cjs b/docs/audits/gitlab-known-host-retirement/reproduce.cjs new file mode 100644 index 00000000000..176613137a8 --- /dev/null +++ b/docs/audits/gitlab-known-host-retirement/reproduce.cjs @@ -0,0 +1,257 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const Module = require('node:module') +const esbuild = require('esbuild') +const { sourcePath, baseline, fixed, sourceHashes, hash } = require('./sources.cjs') + +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function') +const symbol = Symbol.for('orca-known-host-comparison') +const context = { generation: 1, calls: 0, runner: null } +globalThis[symbol] = context +const resultFor = (host) => ({ stdout: `Logged in to ${host} as user`, stderr: '' }) + +async function load(phase) { + const source = phase === 'baseline' ? baseline : fixed + const built = await esbuild.build({ + entryPoints: [sourcePath], + bundle: true, + platform: 'node', + format: 'cjs', + write: false, + plugins: [ + { + name: 'actual-cache-with-fixture-ports', + setup(build) { + build.onLoad({ filter: /gitlab-known-host-probe\.ts$/ }, () => ({ + contents: source, + loader: 'ts' + })) + build.onResolve({ filter: /\/(runner|ssh-git-dispatch)$/ }, (args) => ({ + path: path.basename(args.path), + namespace: 'ports' + })) + build.onLoad({ filter: /.*/, namespace: 'ports' }, (args) => ({ + loader: 'js', + contents: `const context=globalThis[Symbol.for('orca-known-host-comparison')];${ + args.path === 'runner' + ? `exports.glabExecFileAsync=(...args)=>{context.calls++;return context.runner(...args)};` + : `exports.getSshGitProviderGeneration=()=>context.generation;` + }` + })) + } + } + ] + }) + const loaded = new Module(sourcePath, module) + loaded.filename = sourcePath + loaded.paths = module.paths + loaded._compile(built.outputFiles[0].text, sourcePath) + return loaded.exports +} + +async function collect() { + for (let index = 0; index < 6; index++) { + await new Promise((resolve) => setImmediate(resolve)) + global.gc() + } + await new Promise((resolve) => setImmediate(resolve)) +} +async function rememberResult(api) { + const hosts = await api.getGlabKnownHosts('same-connection') + assert.deepEqual(hosts, ['gitlab.com', `host${context.generation}.test`]) + return new WeakRef(hosts) +} +async function retention(api) { + api._resetKnownHostsCache() + context.calls = 0 + context.runner = async () => resultFor(`host${context.generation}.test`) + const refs = [] + for (let generation = 1; generation <= 128; generation++) { + context.generation = generation + refs.push(await rememberResult(api)) + } + await collect() + const retained = refs.filter((ref) => ref.deref() !== undefined).length + await rememberResult(api) + assert.equal(context.calls, 128) + api._resetKnownHostsCache() + await collect() + const afterReset = refs.filter((ref) => ref.deref() !== undefined).length + assert.equal(afterReset, 0) + return { retained, afterReset } +} +async function afterReset(api) { + api._resetKnownHostsCache() + context.calls = 0 + const pending = Promise.withResolvers() + context.runner = () => pending.promise + const old = api.getGlabKnownHosts() + api._resetKnownHostsCache() + context.runner = async () => resultFor('fresh-after-reset.test') + pending.resolve(resultFor('old-before-reset.test')) + assert.deepEqual(await old, ['gitlab.com', 'old-before-reset.test']) + return { hosts: await api.getGlabKnownHosts(), calls: context.calls } +} +async function weakResult(promise) { + return new WeakRef(await promise) +} +async function lateGeneration(api) { + api._resetKnownHostsCache() + context.generation = 1 + const pending = Promise.withResolvers() + context.runner = () => pending.promise + let old = api.getGlabKnownHosts('same-connection') + context.generation = 2 + context.runner = async () => resultFor('replacement-generation.test') + assert.deepEqual(await api.getGlabKnownHosts('same-connection'), [ + 'gitlab.com', + 'replacement-generation.test' + ]) + pending.resolve(resultFor('retired-generation.test')) + const oldResult = await weakResult(old) + old = null + await collect() + const retained = oldResult.deref() !== undefined + assert.deepEqual(await api.getGlabKnownHosts('same-connection'), [ + 'gitlab.com', + 'replacement-generation.test' + ]) + return { oldResultRetained: retained, replacementHostsPreserved: true } +} +async function rememberGeneration(api) { + api._resetKnownHostsCache() + context.runner = () => { + throw new Error('remembered hosts must not probe') + } + const refs = [] + for (let generation = 1; generation <= 16; generation++) { + context.generation = generation + api.rememberGlabKnownHost(`host${generation}.test`, 'same-connection') + refs.push(await rememberResult(api)) + } + await collect() + return refs.filter((ref) => ref.deref() !== undefined).length +} +async function abandonedProbe(api) { + api._resetKnownHostsCache() + const originalNow = Date.now + let now = 1000 + Date.now = () => now + try { + const pending = Promise.withResolvers() + context.runner = () => pending.promise + const old = api.getGlabKnownHosts() + now += 60_001 + context.runner = async () => resultFor('replacement.test') + assert.deepEqual(await api.getGlabKnownHosts(), ['gitlab.com', 'replacement.test']) + pending.resolve(resultFor('abandoned.test')) + await old + return await api.getGlabKnownHosts() + } finally { + Date.now = originalNow + } +} +async function rememberWhilePending(api, fail) { + api._resetKnownHostsCache() + const pending = Promise.withResolvers() + context.runner = () => pending.promise + const old = api.getGlabKnownHosts() + api.rememberGlabKnownHosts(['Remembered.TEST', ' remembered.test ']) + if (fail) { + pending.reject(new Error('controlled auth failure')) + } else { + pending.resolve(resultFor('gitlab.com')) + } + assert.deepEqual(await old, ['gitlab.com', 'remembered.test']) + assert.deepEqual(await api.getGlabKnownHosts(), ['gitlab.com', 'remembered.test']) +} +async function scopeIsolation(api) { + api._resetKnownHostsCache() + const contexts = [ + [undefined, {}], + [undefined, { wslDistro: 'Ubuntu' }], + [undefined, { wslDistro: 'Debian' }], + ['connection-a', {}], + ['connection-b', {}] + ] + for (let index = 0; index < contexts.length; index++) { + context.runner = async () => resultFor(`scope${index}.test`) + assert.deepEqual(await api.getGlabKnownHosts(...contexts[index]), [ + 'gitlab.com', + `scope${index}.test` + ]) + } + context.runner = () => { + throw new Error('cached contexts must not probe') + } + for (let index = 0; index < contexts.length; index++) { + assert.deepEqual(await api.getGlabKnownHosts(...contexts[index]), [ + 'gitlab.com', + `scope${index}.test` + ]) + } +} +async function main() { + const proofHashes = Object.fromEntries( + ['reproduce.cjs', 'sources.cjs', 'fix.patch', 'original-source-hashes.json'].map((file) => [ + file, + hash(fs.readFileSync(path.join(__dirname, file))) + ]) + ) + const report = { sourceHashes, proofHashes, runtime: process.versions, phases: {} } + for (const phase of ['baseline', 'fixed']) { + const api = await load(phase) + const retained = await retention(api) + const rememberedGenerationsRetained = await rememberGeneration(api) + const oldGeneration = await lateGeneration(api) + const reset = await afterReset(api) + const abandoned = await abandonedProbe(api) + await rememberWhilePending(api, false) + await rememberWhilePending(api, true) + await scopeIsolation(api) + assert.equal(retained.retained, phase === 'baseline' ? 128 : 1) + assert.equal(rememberedGenerationsRetained, phase === 'baseline' ? 16 : 1) + assert.equal(oldGeneration.oldResultRetained, phase === 'baseline') + assert.deepEqual( + reset.hosts, + phase === 'baseline' + ? ['gitlab.com', 'old-before-reset.test'] + : ['gitlab.com', 'fresh-after-reset.test'] + ) + assert.deepEqual( + abandoned, + phase === 'baseline' + ? ['gitlab.com', 'replacement.test', 'abandoned.test'] + : ['gitlab.com', 'replacement.test'] + ) + report.phases[phase] = { + retained, + rememberedGenerationsRetained, + oldGeneration, + reset, + abandoned, + rememberedSuccessAndFailure: 'passed', + nativeWslConnectionIsolation: 'passed' + } + api._resetKnownHostsCache() + } + fs.writeFileSync( + process.argv[2] || path.join(__dirname, 'results.json'), + `${JSON.stringify(report, null, 2)}\n` + ) + console.log(JSON.stringify(report.phases, null, 2)) +} +main() + .catch((error) => { + console.error(error) + process.exitCode = 1 + }) + .finally(() => { + delete globalThis[symbol] + }) +setTimeout(() => { + console.error('fixture deadline') + process.exit(2) +}, 10000).unref() diff --git a/docs/audits/gitlab-known-host-retirement/results.json b/docs/audits/gitlab-known-host-retirement/results.json new file mode 100644 index 00000000000..41495555ade --- /dev/null +++ b/docs/audits/gitlab-known-host-retirement/results.json @@ -0,0 +1,88 @@ +{ + "sourceHashes": { + "src/main/gitlab/gitlab-known-host-probe.ts": { + "baseline": "4f9651c5a383438aa968aec082c200a3371e2f4317372125aa11aa6938793c25", + "fixed": "ecf0d67f68cf4b7b1bc7d6ff19a1815a8b95fc9721962d3cd873702f98831bad" + }, + "src/main/git/coalesced-probe.ts": "e5a13820a7d8b5f501a3804961ea26526bd6ad9144ecf597d5d83a70d81885e1", + "src/main/git/remote-ref-probe-cache.ts": "d5cbfd97b30e72b03c0d28d8b9e75a2ae1f9e3efc9f5b5570c78e0742c0582dd", + "src/main/gitlab/project-ref-parser.ts": "0f8b6758e6a162a58f436ca4213addc42bf6ceb3fcc2e63e8c7402c844af9303", + "src/main/gitlab/gitlab-known-host-retirement.test.ts": "c6dedbac0462cae22903805d0a89be397a2b9d122a29640b6a07a2d274f8e98e" + }, + "proofHashes": { + "reproduce.cjs": "19f49406685923b050a99fbc92b02b9f6f5f8d8f308dcc14336415a0988679cc", + "sources.cjs": "2ee8ac0f295d65f16489da2b0c03800b9db4e88c83ecfb63ae7be1ec3ea6c148", + "fix.patch": "695c929087006b3406ce2c30a7ab783d88af3f9675f4ea320dc7f98b8476be9f", + "original-source-hashes.json": "f9770d9f0a87afc9231eef6af99e8ded33348fdc21ba46a70b1d5d3388874b97" + }, + "runtime": { + "node": "26.6.0", + "acorn": "8.17.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.3", + "libffi": "3.7.1", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "147", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "3.6.3", + "simdjson": "4.6.6", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2026a", + "undici": "8.9.0", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "14.6.202.34-node.26", + "zlib": "1.2.12", + "zstd": "1.5.7" + }, + "phases": { + "baseline": { + "retained": { + "retained": 128, + "afterReset": 0 + }, + "rememberedGenerationsRetained": 16, + "oldGeneration": { + "oldResultRetained": true, + "replacementHostsPreserved": true + }, + "reset": { + "hosts": ["gitlab.com", "old-before-reset.test"], + "calls": 1 + }, + "abandoned": ["gitlab.com", "replacement.test", "abandoned.test"], + "rememberedSuccessAndFailure": "passed", + "nativeWslConnectionIsolation": "passed" + }, + "fixed": { + "retained": { + "retained": 1, + "afterReset": 0 + }, + "rememberedGenerationsRetained": 1, + "oldGeneration": { + "oldResultRetained": false, + "replacementHostsPreserved": true + }, + "reset": { + "hosts": ["gitlab.com", "fresh-after-reset.test"], + "calls": 2 + }, + "abandoned": ["gitlab.com", "replacement.test"], + "rememberedSuccessAndFailure": "passed", + "nativeWslConnectionIsolation": "passed" + } + } +} diff --git a/docs/audits/gitlab-known-host-retirement/sources.cjs b/docs/audits/gitlab-known-host-retirement/sources.cjs new file mode 100644 index 00000000000..1effdb26ecf --- /dev/null +++ b/docs/audits/gitlab-known-host-retirement/sources.cjs @@ -0,0 +1,30 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const crypto = require('node:crypto') +const { parsePatch, reversePatch, applyPatch } = require('diff') + +const root = path.resolve(__dirname, '../../..') +const relativePath = 'src/main/gitlab/gitlab-known-host-probe.ts' +const sourcePath = path.join(root, relativePath) +const fixed = fs.readFileSync(sourcePath, 'utf8') +const patches = parsePatch(fs.readFileSync(path.join(__dirname, 'fix.patch'), 'utf8')) +assert.equal(patches.length, 1) +assert.equal(patches[0].newFileName, `b/${relativePath}`) +const baseline = applyPatch(fixed, reversePatch(patches[0])) +assert.notEqual(baseline, false, 'Current source must reverse exactly to the original cache') +const hash = (value) => crypto.createHash('sha256').update(value).digest('hex') +assert.equal(hash(baseline), require('./original-source-hashes.json')[relativePath]) +const sourceHashes = { + [relativePath]: { baseline: hash(baseline), fixed: hash(fixed) }, + ...Object.fromEntries( + [ + 'src/main/git/coalesced-probe.ts', + 'src/main/git/remote-ref-probe-cache.ts', + 'src/main/gitlab/project-ref-parser.ts', + 'src/main/gitlab/gitlab-known-host-retirement.test.ts' + ].map((file) => [file, hash(fs.readFileSync(path.join(root, file)))]) + ) +} + +module.exports = { root, sourcePath, baseline, fixed, sourceHashes, hash } diff --git a/src/main/gitlab/gitlab-known-host-probe.ts b/src/main/gitlab/gitlab-known-host-probe.ts index 752e1b0291e..d328cd38cac 100644 --- a/src/main/gitlab/gitlab-known-host-probe.ts +++ b/src/main/gitlab/gitlab-known-host-probe.ts @@ -12,7 +12,10 @@ export type LocalGitExecOptions = { const GLAB_KNOWN_HOSTS_TIMEOUT_MS = 10_000 const UNAUTHENTICATED_HOSTS_MAX_ENTRIES = 128 -const knownHostsCacheByExecutionContext = new Map() +const knownHostsCacheByExecutionContext = new Map< + string, + { key: string; hosts: readonly string[] } +>() const knownHostsInFlightByExecutionContext: CoalescedProbes = new Map() const unauthenticatedHostExpiries = new Map() @@ -27,6 +30,19 @@ function knownHostsExecutionKey( return localGitOptions.wslDistro ? `wsl:${localGitOptions.wslDistro}` : 'native' } +function knownHostsCacheContext( + connectionId?: string | null, + localGitOptions: LocalGitExecOptions = {} +): { key: string; cacheKey: string } { + const key = knownHostsExecutionKey(connectionId, localGitOptions) + const cacheKey = connectionId ? `connection:${connectionId}` : key + const cached = knownHostsCacheByExecutionContext.get(cacheKey) + if (cached && cached.key !== key) { + knownHostsCacheByExecutionContext.delete(cacheKey) + } + return { key, cacheKey } +} + /** @internal - exposed for tests only */ export function _resetKnownHostsCache(): void { knownHostsCacheByExecutionContext.clear() @@ -103,8 +119,8 @@ export function rememberGlabKnownHosts( connectionId?: string | null, localGitOptions: LocalGitExecOptions = {} ): void { - const key = knownHostsExecutionKey(connectionId, localGitOptions) - const cached = knownHostsCacheByExecutionContext.get(key) ?? DEFAULT_GITLAB_HOSTS + const { key, cacheKey } = knownHostsCacheContext(connectionId, localGitOptions) + const cached = knownHostsCacheByExecutionContext.get(cacheKey)?.hosts ?? DEFAULT_GITLAB_HOSTS const seen = new Set(cached.map(normalizeGitLabHost)) const additions: string[] = [] for (const host of hosts) { @@ -121,27 +137,29 @@ export function rememberGlabKnownHosts( if (additions.length === 0) { return } - knownHostsCacheByExecutionContext.set(key, [...cached, ...additions]) + knownHostsCacheByExecutionContext.set(cacheKey, { key, hosts: [...cached, ...additions] }) } export async function getGlabKnownHosts( connectionId?: string | null, localGitOptions: LocalGitExecOptions = {} ): Promise { - const key = knownHostsExecutionKey(connectionId, localGitOptions) - const cached = knownHostsCacheByExecutionContext.get(key) + const { key, cacheKey } = knownHostsCacheContext(connectionId, localGitOptions) + const cached = knownHostsCacheByExecutionContext.get(cacheKey)?.hosts if (cached) { return cached } // Why: only join a probe still young enough to answer, so a wedged one cannot // pin every later retry for the life of the process (P1-D). - return runCoalescedProbe(knownHostsInFlightByExecutionContext, key, () => - probeGlabKnownHosts(key, connectionId, localGitOptions) + return runCoalescedProbe(knownHostsInFlightByExecutionContext, key, (ownsKey) => + probeGlabKnownHosts(key, cacheKey, ownsKey, connectionId, localGitOptions) ) } async function probeGlabKnownHosts( key: string, + cacheKey: string, + ownsKey: () => boolean, connectionId?: string | null, localGitOptions: LocalGitExecOptions = {} ): Promise { @@ -160,13 +178,17 @@ async function probeGlabKnownHosts( ...(localGitOptions.admissionTier ? { admissionTier: localGitOptions.admissionTier } : {}) }) const hosts = parseGlabAuthStatusHosts(`${stdout}\n${stderr}`) - const remembered = knownHostsCacheByExecutionContext.get(key) ?? [] + const cached = knownHostsCacheByExecutionContext.get(cacheKey) + const remembered = cached?.key === key ? cached.hosts : [] const merged = Array.from(new Set([...DEFAULT_GITLAB_HOSTS, ...remembered, ...hosts])) - knownHostsCacheByExecutionContext.set(key, merged) + if (ownsKey() && knownHostsExecutionKey(connectionId, localGitOptions) === key) { + knownHostsCacheByExecutionContext.set(cacheKey, { key, hosts: merged }) + } return merged } catch { // Keep failures uncached so auth or tunnel recovery is discovered later. - return knownHostsCacheByExecutionContext.get(key) ?? [...DEFAULT_GITLAB_HOSTS] + const cached = knownHostsCacheByExecutionContext.get(cacheKey) + return cached?.key === key ? cached.hosts : [...DEFAULT_GITLAB_HOSTS] } } diff --git a/src/main/gitlab/gitlab-known-host-retirement.test.ts b/src/main/gitlab/gitlab-known-host-retirement.test.ts new file mode 100644 index 00000000000..60bb2be13b2 --- /dev/null +++ b/src/main/gitlab/gitlab-known-host-retirement.test.ts @@ -0,0 +1,171 @@ +import { afterEach, beforeEach, expect, it, vi } from 'vitest' + +const { execute, generations } = vi.hoisted(() => ({ + execute: vi.fn(), + generations: new Map() +})) +vi.mock('../git/runner', () => ({ glabExecFileAsync: execute })) +vi.mock('../providers/ssh-git-dispatch', () => ({ + getSshGitProviderGeneration: (connectionId: string) => generations.get(connectionId) ?? 0 +})) + +import { + _resetKnownHostsCache, + getGlabKnownHosts, + rememberGlabKnownHost +} from './gitlab-known-host-probe' +import { PROBE_COALESCE_STALE_MS } from '../git/coalesced-probe' + +const response = (host: string) => ({ stdout: `Logged in to ${host} as user`, stderr: '' }) +const deferred = () => Promise.withResolvers>() + +async function collect(): Promise { + if (typeof globalThis.gc !== 'function') { + throw new Error('Run with the repository Vitest --expose-gc config') + } + for (let index = 0; index < 6; index++) { + await new Promise((resolve) => setImmediate(resolve)) + globalThis.gc() + } + await new Promise((resolve) => setImmediate(resolve)) +} + +async function weakResult(connectionId: string): Promise> { + return new WeakRef(await getGlabKnownHosts(connectionId)) +} + +beforeEach(() => { + _resetKnownHostsCache() + generations.clear() + execute.mockReset() +}) +afterEach(() => { + _resetKnownHostsCache() + vi.restoreAllMocks() +}) + +it('releases successful host arrays from superseded SSH generations', async () => { + const results: WeakRef[] = [] + for (let generation = 1; generation <= 32; generation++) { + generations.set('connection', generation) + execute.mockResolvedValue(response(`host${generation}.test`)) + results.push(await weakResult('connection')) + } + await collect() + expect(results.filter((result) => result.deref() !== undefined)).toHaveLength(1) + await expect(getGlabKnownHosts('connection')).resolves.toEqual(['gitlab.com', 'host32.test']) + expect(execute).toHaveBeenCalledTimes(32) +}) + +it('retires remembered generations without requiring an auth-status probe', async () => { + const results: WeakRef[] = [] + for (let generation = 1; generation <= 16; generation++) { + generations.set('connection', generation) + rememberGlabKnownHost(`host${generation}.test`, 'connection') + results.push(await weakResult('connection')) + } + await collect() + expect(results.filter((result) => result.deref() !== undefined)).toHaveLength(1) + await expect(getGlabKnownHosts('connection')).resolves.toEqual(['gitlab.com', 'host16.test']) + expect(execute).not.toHaveBeenCalled() +}) + +it('does not retain a delayed old-generation result after a replacement answers', async () => { + const old = deferred() + generations.set('connection', 1) + execute.mockReturnValueOnce(old.promise) + const oldResult = weakResult('connection') + generations.set('connection', 2) + execute.mockResolvedValueOnce(response('replacement.test')) + await expect(getGlabKnownHosts('connection')).resolves.toEqual(['gitlab.com', 'replacement.test']) + old.resolve(response('retired.test')) + const reference = await oldResult + await collect() + expect(reference.deref()).toBeUndefined() + await expect(getGlabKnownHosts('connection')).resolves.toEqual(['gitlab.com', 'replacement.test']) + expect(execute).toHaveBeenCalledTimes(2) +}) + +it('does not repopulate an explicitly reset cache from an earlier probe', async () => { + const old = deferred() + execute.mockReturnValueOnce(old.promise) + const oldResult = getGlabKnownHosts() + _resetKnownHostsCache() + old.resolve(response('before-reset.test')) + await expect(oldResult).resolves.toEqual(['gitlab.com', 'before-reset.test']) + execute.mockResolvedValueOnce(response('after-reset.test')) + await expect(getGlabKnownHosts()).resolves.toEqual(['gitlab.com', 'after-reset.test']) + expect(execute).toHaveBeenCalledTimes(2) +}) + +it('keeps a post-reset successor joinable when the old probe settles first', async () => { + const old = deferred() + const next = deferred() + execute.mockReturnValueOnce(old.promise).mockReturnValueOnce(next.promise) + const oldResult = getGlabKnownHosts() + _resetKnownHostsCache() + const nextResult = getGlabKnownHosts() + old.resolve(response('before-reset.test')) + await oldResult + let joinedSettled = false + const joined = getGlabKnownHosts().then((hosts) => { + joinedSettled = true + return hosts + }) + await Promise.resolve() + expect(joinedSettled).toBe(false) + next.resolve(response('after-reset.test')) + await expect(nextResult).resolves.toEqual(['gitlab.com', 'after-reset.test']) + await expect(joined).resolves.toEqual(['gitlab.com', 'after-reset.test']) + expect(execute).toHaveBeenCalledTimes(2) +}) + +it('prevents an abandoned same-generation probe from publishing over its successor', async () => { + const clock = vi.spyOn(Date, 'now').mockReturnValue(1000) + const old = deferred() + execute.mockReturnValueOnce(old.promise) + const oldResult = getGlabKnownHosts('connection') + clock.mockReturnValue(1000 + PROBE_COALESCE_STALE_MS + 1) + execute.mockResolvedValueOnce(response('replacement.test')) + await expect(getGlabKnownHosts('connection')).resolves.toEqual(['gitlab.com', 'replacement.test']) + old.resolve(response('abandoned.test')) + await oldResult + await expect(getGlabKnownHosts('connection')).resolves.toEqual(['gitlab.com', 'replacement.test']) + expect(execute).toHaveBeenCalledTimes(2) +}) + +it('keeps native, WSL and other connection caches when one generation changes', async () => { + execute + .mockResolvedValueOnce(response('native.test')) + .mockResolvedValueOnce(response('ubuntu.test')) + .mockResolvedValueOnce(response('debian.test')) + .mockResolvedValueOnce(response('connection-a.test')) + .mockResolvedValueOnce(response('connection-b.test')) + const native = await getGlabKnownHosts() + const ubuntu = await getGlabKnownHosts(undefined, { wslDistro: 'Ubuntu' }) + const debian = await getGlabKnownHosts(undefined, { wslDistro: 'Debian' }) + await getGlabKnownHosts('connection-a') + const other = await getGlabKnownHosts('connection-b') + generations.set('connection-a', 1) + rememberGlabKnownHost('replacement.test', 'connection-a') + await expect(getGlabKnownHosts('connection-a')).resolves.toEqual([ + 'gitlab.com', + 'replacement.test' + ]) + await expect(getGlabKnownHosts()).resolves.toBe(native) + await expect(getGlabKnownHosts(undefined, { wslDistro: 'Ubuntu' })).resolves.toBe(ubuntu) + await expect(getGlabKnownHosts(undefined, { wslDistro: 'Debian' })).resolves.toBe(debian) + await expect(getGlabKnownHosts('connection-b')).resolves.toBe(other) + expect(execute).toHaveBeenCalledTimes(5) +}) + +it('does not serve a retired generation after the current probe fails', async () => { + execute.mockResolvedValueOnce(response('retired.test')) + await getGlabKnownHosts('connection') + generations.set('connection', 1) + execute.mockRejectedValueOnce(new Error('current host unavailable')) + await expect(getGlabKnownHosts('connection')).resolves.toEqual(['gitlab.com']) + execute.mockResolvedValueOnce(response('current.test')) + await expect(getGlabKnownHosts('connection')).resolves.toEqual(['gitlab.com', 'current.test']) + expect(execute).toHaveBeenCalledTimes(3) +}) From fbfe3a2e74043673ccc928554f6fd667493cbb4a Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:12:11 -0700 Subject: [PATCH 053/168] fix: release Codex prompt claims when their turns complete (#21138) Co-authored-by: m4air --- .../codex-prompt-claim-retention/README.md | 53 + .../before.config.mjs | 24 + .../electron-results.json | 1239 +++++++++++++++++ .../codex-prompt-claim-retention/fix.patch | 11 + .../node-results.json | 1238 ++++++++++++++++ .../reproduce.cjs | 111 ++ .../codex-prompt-claim-retention/scenario.cjs | 215 +++ .../source-versions.json | 54 + .../codex-prompt-claim-retention/sources.cjs | 29 + .../validation.json | 69 + .../codex-prompt-registry-retention.test.ts | 110 ++ src/main/codex/codex-prompt-registry.ts | 2 +- 12 files changed, 3154 insertions(+), 1 deletion(-) create mode 100644 docs/audits/codex-prompt-claim-retention/README.md create mode 100644 docs/audits/codex-prompt-claim-retention/before.config.mjs create mode 100644 docs/audits/codex-prompt-claim-retention/electron-results.json create mode 100644 docs/audits/codex-prompt-claim-retention/fix.patch create mode 100644 docs/audits/codex-prompt-claim-retention/node-results.json create mode 100644 docs/audits/codex-prompt-claim-retention/reproduce.cjs create mode 100644 docs/audits/codex-prompt-claim-retention/scenario.cjs create mode 100644 docs/audits/codex-prompt-claim-retention/source-versions.json create mode 100644 docs/audits/codex-prompt-claim-retention/sources.cjs create mode 100644 docs/audits/codex-prompt-claim-retention/validation.json create mode 100644 src/main/codex/codex-prompt-registry-retention.test.ts diff --git a/docs/audits/codex-prompt-claim-retention/README.md b/docs/audits/codex-prompt-claim-retention/README.md new file mode 100644 index 00000000000..5a617232d0b --- /dev/null +++ b/docs/audits/codex-prompt-claim-retention/README.md @@ -0,0 +1,53 @@ +# Codex prompt claims retained after turn completion + +Confirmed cancellation keeps a prompt claim until its turn completes. If the prompt's lookup entries are evicted or replaced first, the old `clearTurn()` cannot find it. The separate claims map then retains the prompt until the whole session is cleared. + +The fix includes claimed prompts in the existing exact-thread/turn cleanup. Existing turn matching and `forget()` object-identity checks preserve a replacement prompt's authority. Registry limits and cancellation timing are unchanged. + +## Source ownership and reachability + +1. `codex-structured-provider-events.ts:57` registers incoming prompt requests and publishes them through the translator. `codex-structured-session-acquire.ts:95` binds the translator's turn cleanup to the session registry. +2. `codex-structured-prompt-ownership.ts:33` acquires the claim. Confirmed cancellation deliberately leaves it owned; unsuccessful/unconfirmed cancellation releases it. The actual `CodexStructuredTurnCancellation` invokes the confirmation callback after the injected interrupt transport acknowledges success. +3. `codex-prompt-registry.ts:258` trims the address and journal-binding maps independently. Neither trim removes claims. Replacing the same journal address can similarly leave the old claim without a lookup entry. +4. A later `turn/completed` goes through `translateCodexNotification`, the journal translator and `settleCodexJournalTurn`. Accepted lifecycle settlement invokes `clearPromptTurn` at `codex-structured-journal-settlement.ts:170`. +5. The old cleanup enumerates only address/binding values. The fix also enumerates `claims.keys()`, still filtering by the exact thread/turn. `forget()` deletes replacement lookup entries only when they contain that same prompt object. + +The safely expired owner is the claim for the terminal turn whose cleanup has been admitted. Live claims survive unrelated turn cleanup and registry eviction. A refused lifecycle settlement does not clear them. + +## Reproduce + +From the worktree root, using installed dependencies: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/codex-prompt-claim-retention/reproduce.cjs +``` + +For Electron, use its installed executable with `ELECTRON_RUN_AS_NODE=1`, the same flags and script. The final optional argument selects the report path; the default is `node-results.json` beside the script. No Electron window is created. + +`sources.cjs` reverses `fix.patch` against current source and rejects a baseline hash mismatch. It neither reads a previous commit to reconstruct the implementation nor changes product files. The proof bundles actual source in memory. Each report records effective source and bundle hashes, dependency hashes and runtime versions. Only the requested report is written. + +The fixture uses the actual registry, server-request delivery, cancellation ownership function, cancellation class, journal translator and delayed notification delivery. It injects an accepting journal sink, interrupt transport and primary-turn lookup. Prompts belong to child threads, so the production child-turn cancellation branch does not enumerate or terminate processes. Every injected process helper throws if unexpectedly called. + +The sequence creates 32 ordinary small prompt objects, confirms their cancellations, admits 256 unrelated prompts to evict lookup entries, then completes the original exact turns. WeakRefs count prompt liveness after forced collections. No large payload is attached. The 20-second deadline and 192 MiB heap limit bound the proof. + +## Results + +Both [Node 26.6.0](./node-results.json) and [Electron 43.7.0 / Node 24.21.0](./electron-results.json) produced: + +| Observation | Before | After | +| -------------------------------------------------------------------------- | -----: | ----: | +| Retained cancelled prompts after lookup eviction, before completion | 32 | 32 | +| Retained after exact turn completion | 32 | 0 | +| Retained after all lookup maps become empty | 32 | 0 | +| Retained after session clear | 0 | 0 | +| Old prompt retained after same-address replacement and old-turn completion | 1 | 0 | + +Ordinary completion releases its prompt on both versions. Wrong-thread, wrong-turn and refused-completion controls preserve claims. The replacement prompt and its active claim remain valid after old-turn cleanup on both versions. + +The four regression tests cover 32 evicted claims, replacement authority, a compatibility turn digest and session cleanup. Applying the reversed source produces three expected failures; the session-clear control passes. Existing prompt ownership/reply tests also pass on the reversed source. Current source passes 71 tests across six files plus Node, CLI and Web typechecks; [validation.json](./validation.json) records commands and other checks. + +## Limits + +This is a code-level lifetime defect. The request/completion ordering is deliberately injected; this is not a capture of Codex emitting that sequence or an affected host. Counts do not measure ordinary prompt bytes or establish a growth rate. It does not identify the cause of #19831 or any other incident. + +[source-versions.json](./source-versions.json) records matching baseline source at the named main revision. No historical application runtime was reproduced. The fix uses existing turn ownership and identity checks; it adds no arbitrary eviction policy. diff --git a/docs/audits/codex-prompt-claim-retention/before.config.mjs b/docs/audits/codex-prompt-claim-retention/before.config.mjs new file mode 100644 index 00000000000..1a6483dfb9d --- /dev/null +++ b/docs/audits/codex-prompt-claim-retention/before.config.mjs @@ -0,0 +1,24 @@ +import { resolve } from 'node:path' +import { createRequire } from 'node:module' +import { defineConfig, mergeConfig } from 'vitest/config' +import baseConfig from '../../../config/vitest.config.ts' + +const loadSources = createRequire(import.meta.url)( + resolve('docs/audits/codex-prompt-claim-retention/sources.cjs') +) +const { before } = loadSources() +export default mergeConfig( + baseConfig, + defineConfig({ + plugins: [ + { + name: 'codex-claim-before-fix', + enforce: 'pre', + transform(_code, id) { + const source = before.get(resolve(id.split('?')[0])) + return source === undefined ? undefined : { code: source, map: null } + } + } + ] + }) +) diff --git a/docs/audits/codex-prompt-claim-retention/electron-results.json b/docs/audits/codex-prompt-claim-retention/electron-results.json new file mode 100644 index 00000000000..56116e4e20d --- /dev/null +++ b/docs/audits/codex-prompt-claim-retention/electron-results.json @@ -0,0 +1,1239 @@ +{ + "capturedAt": "2026-09-17T02:39:39.480Z", + "runtime": { + "node": "24.21.0", + "acorn": "8.18.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.2", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "148", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "0.0.0", + "simdjson": "4.6.7", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2025c", + "undici": "7.29.1", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "15.0.245.31-electron.0", + "zlib": "1.3.2.1-motley", + "zstd": "1.6.0", + "electron": "43.7.0", + "chrome": "150.0.7871.250" + }, + "scope": "Actual registry, server-request translation, cancellation ownership/cancellation class, journal translator, delayed turn completion. Injected accepted sink, interrupt transport, and compaction lookup. No provider, process enumeration/termination, or host data. Bundles load in memory; only the requested report is written.", + "sourceHashes": { + "src/main/codex/codex-prompt-registry.ts": { + "before": "254c530216f47abfc3572bb1689161ad3c10775af2cdceda7e94f443e52c0691", + "after": "3d33f98215b9547d661c0a4f6aa8b9a8a6a88fa953d90652cb3fb9953425a539" + } + }, + "countsOnly": true, + "noPayloadAmplification": true, + "results": { + "original": { + "ordinaryAfterCompletion": 0, + "cancelledPrompts": 32, + "sizesAfterEviction": { + "prompts": 128, + "journalBindings": 256 + }, + "afterEvictionBeforeCompletion": 32, + "afterExactTurnCompletion": 32, + "afterAllLookupMapsEmpty": 32, + "afterSessionClear": 0, + "oldAfterReplacementCompletion": 1, + "replacementClaimPreserved": true, + "wrongThreadPreserved": true, + "wrongTurnPreserved": true, + "rejectedCompletionPreserved": true, + "successfulInterruptRequests": 34 + }, + "candidate": { + "ordinaryAfterCompletion": 0, + "cancelledPrompts": 32, + "sizesAfterEviction": { + "prompts": 128, + "journalBindings": 256 + }, + "afterEvictionBeforeCompletion": 32, + "afterExactTurnCompletion": 0, + "afterAllLookupMapsEmpty": 0, + "afterSessionClear": 0, + "oldAfterReplacementCompletion": 0, + "replacementClaimPreserved": true, + "wrongThreadPreserved": true, + "wrongTurnPreserved": true, + "rejectedCompletionPreserved": true, + "successfulInterruptRequests": 34 + } + }, + "versions": { + "original": { + "bundleSha256": "e060beb8d88044d6abe07134880d9ddcbb8487464c963d0cce336454e789bcc8", + "dependencies": [ + { + "path": "src/main/native-chat/agent-session-journal/journal-payload-bounds.ts", + "sha256": "a421da6c6eee9346746a69f8ba7ff966ce81969dfa0a93f58784cac27cf8cdad" + }, + { + "path": "src/shared/agent-session-wire-refusals.ts", + "sha256": "25123cce70418deb3d2f75cf5c3b7431aa6a8c3b834f022cd697230345c595ea" + }, + { + "path": "src/shared/agent-session-background-task-wire.ts", + "sha256": "6a756544bb07864d5a6e4573992f4b3bb0d0788acb686c16da177095be23167c" + }, + { + "path": "src/shared/agent-session-wire.ts", + "sha256": "466b641465f92957dafe873c68d4cb42f50aeb0768a111397050b50aa96ac64f" + }, + { + "path": "src/main/codex/codex-prompt-registry-bounds.ts", + "sha256": "f6bfd21bcc2ccf7005a93a4764d17235887f11380b0b29301410b0f291a5c618" + }, + { + "path": "src/main/codex/codex-item-field-readers.ts", + "sha256": "aa4b178c562a63995b7de63c6f5daaf7b6b610fb163fb0a357cc230314076323" + }, + { + "path": "src/main/codex/codex-prompt-registry.ts", + "sha256": "254c530216f47abfc3572bb1689161ad3c10775af2cdceda7e94f443e52c0691" + }, + { + "path": "src/main/native-chat/agent-session-wire/structured-agent-session-adapter.ts", + "sha256": "9a7bb24e419dbd41f4aaaa20b87dd82859579d663ebb800fc605df4b471749ef" + }, + { + "path": "src/main/codex/codex-structured-prompt-replies.ts", + "sha256": "7b927e9910031ee80ab885ce1d713133d3ee614c5b6d460a5c91ff3768d9cb2e" + }, + { + "path": "src/shared/child-process/cancel-process-acquisition.ts", + "sha256": "709a04316da5f3528e33e02bbb6f054012714efc73628a04fd08289eae0c42d5" + }, + { + "path": "src/main/codex/codex-structured-acquisition-window.ts", + "sha256": "6828970d450a53fcb30ecc0ecb20d8452395bf579cc2295ee53415c299d039a4" + }, + { + "path": "src/main/codex/codex-structured-session-state.ts", + "sha256": "271837e87efac55238342a26ccdf350d9eb663b1d38a2553886429d1376ab1f4" + }, + { + "path": "src/main/codex/codex-structured-prompt-ownership.ts", + "sha256": "8d86f3784b79a1470e4002527bdfa505efe2762cba865de7716442f7d9730013" + }, + { + "path": "src/shared/child-process/windows-command-line.ts", + "sha256": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca" + }, + { + "path": "src/shared/child-process/windows-cmd-shim-resolution.ts", + "sha256": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3" + }, + { + "path": "src/shared/child-process/spawn-resolution.ts", + "sha256": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc" + }, + { + "path": "src/shared/child-process/process-tree-kill-gate.ts", + "sha256": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab" + }, + { + "path": "src/shared/child-process/process-tree-termination.ts", + "sha256": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120" + }, + { + "path": "src/shared/child-process/bounded-output-sink.ts", + "sha256": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865" + }, + { + "path": "src/shared/child-process/child-termination-reporter.ts", + "sha256": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0" + }, + { + "path": "src/shared/child-process/process-spec.ts", + "sha256": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289" + }, + { + "path": "src/shared/child-process/run-process.ts", + "sha256": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad" + }, + { + "path": "src/shared/child-process/retryable-process-exit-proof.ts", + "sha256": "411a39e4508763d8c4ef331bd16e88c1fe497f03188493241afcce6eab0a6e65" + }, + { + "path": "src/main/codex/codex-app-server-posix-supervisor.ts", + "sha256": "ccd4ff7a43d3ba92b9becab45304d5f5522f5c1d5aa53d379fdd47845d0fe545" + }, + { + "path": "src/main/codex/codex-app-server-capability-signal.ts", + "sha256": "43cf352901aeff4ed9143f51f224770f3dc92bf79dcfac6875bbc64105184c2f" + }, + { + "path": "src/main/codex/codex-process-exit-deadline.ts", + "sha256": "952cbb7c8778b8187ff443f8c045be53c14c3778af1b2ec77a3af5cd3ff13364" + }, + { + "path": "src/shared/system-cli-install-dirs.ts", + "sha256": "635b59f51022c9f31bf7720385b343018d4ce849220d422115e74f5351a9f21d" + }, + { + "path": "src/shared/node-cli-command-resolution.ts", + "sha256": "5eecee0c0d6296b962315c2ef2ae228a1bb1b6a7449f46d5afe3977f0af1e145" + }, + { + "path": "src/main/codex/codex-app-server-process-tree-kill.ts", + "sha256": "0a370b1d40509aa967634d6551f6baf5462b648910ddb79c2624555a84116b0b" + }, + { + "path": "src/shared/main-process-ndjson-framer.ts", + "sha256": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490" + }, + { + "path": "src/main/codex/codex-app-server-record-reader.ts", + "sha256": "b4d600293228ae2429090facc3db6f98dff449f2c3d82f35a0bea11fe4d70048" + }, + { + "path": "src/main/codex/codex-app-server-session.ts", + "sha256": "9adfbab8e229d3998d973ad9abfe647015ff21a5517fa6e5c7b32acf0a446eb8" + }, + { + "path": "src/main/codex/codex-app-server-exit-error.ts", + "sha256": "d58cdd97ae3ad4a786c7c4a0b1062ee3c5bb15b0943046dda7fff4b51ede21d1" + }, + { + "path": "src/main/codex/codex-app-server-handshake.ts", + "sha256": "3dc86d5961076713cb67ff58f593ca0fe848cd2f6b5a3a0a5da83773f49e6516" + }, + { + "path": "src/main/codex/codex-app-server-handshake-exit-proof.ts", + "sha256": "01832f6ab97bb68566221151d6a88e03cd5461f699991ad0e2c1f52573541ca1" + }, + { + "path": "src/shared/crash-reporting-diagnostic-bundle.ts", + "sha256": "2ac76a8941d4e8ae8d4e0a95ac9e10102602670e4c8fdbcb88a03b014903d1ba" + }, + { + "path": "src/shared/crash-report-signature-lines.ts", + "sha256": "564936f235f0c9faefc0c9f18f2fcbc588e5bef69ecd1071baac5a30a7c662a8" + }, + { + "path": "src/shared/posix-wait-status.ts", + "sha256": "1eb72525881baa2815548d31d2b83aaecf88e5dfd0258dbcaa5cda1e8fdcd69f" + }, + { + "path": "src/shared/crash-report-exit-code.ts", + "sha256": "a73f4e6fce72f11f5eec32c2a22d2e749f330f434aef20cec6dc03dbde55ebc6" + }, + { + "path": "src/shared/react-update-depth-attribution.ts", + "sha256": "15fd538fa4953088b63d9307fc575d4f795614b445a558340d80d61bb4c50929" + }, + { + "path": "src/shared/crash-report-attribution-lines.ts", + "sha256": "3db6cabca0444343347a90e6ec71a015a79489097e2484202cf2b57232483a6e" + }, + { + "path": "src/shared/crash-report-redaction.ts", + "sha256": "afd00bd49c77398425ee481070818e98067e90bb472a7454c571bf77ceee9c7f" + }, + { + "path": "src/shared/crash-reporting.ts", + "sha256": "e9749318038d145b7ae1819ffed6b285815c502d0eba13181c48b73b1bd3e918" + }, + { + "path": "src/main/observability/redactor.ts", + "sha256": "59190309dbef22508e3f2ef27b0190093d5aaab8cce146d6d49a89eb9d0a2b73" + }, + { + "path": "src/main/observability/tracer.ts", + "sha256": "82b6f4ec0be07b6aa612038b21b9f67b0ef199f694d29dc9f7b54b55dba9776e" + }, + { + "path": "src/main/crash-reporting/crash-breadcrumb-store.ts", + "sha256": "8848be8a897e0d1a214dbbaf87a0377d493ee83358763c0fc286e693889b2cb9" + }, + { + "path": "src/main/crash-reporting/main-process-lifecycle-identity.ts", + "sha256": "797cd37e5af6ec71f91636bc779274572a62ea67e5fa2874bb1f2f771020c950" + }, + { + "path": "src/main/crash-reporting/durable-crash-breadcrumb.ts", + "sha256": "477e28157e119f3bd85bc77d4adb185d36e44103b8a087d680715ce71b05dea7" + }, + { + "path": "src/main/crash-reporting/self-initiated-tree-kill-log.ts", + "sha256": "eb28b4a513a3cbc30586df30569adf29d828f059522ebe6c751d55b233c13987" + }, + { + "path": "src/shared/app-environment.ts", + "sha256": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140" + }, + { + "path": "src/main/orca-chromium-process-pids.ts", + "sha256": "960a15f005ff997fa379972a8c2abe3410b78c6facee449e8a5350754cfce965" + }, + { + "path": "src/main/own-chromium-tree-kill-guard.ts", + "sha256": "3a59cccd8cf923506aacad58f1c5d11b49f47a21e6c6dee8559912ee3c5cefb5" + }, + { + "path": "src/main/windows-process-tree-kill.ts", + "sha256": "83236378e93e3a15189ca02febf6b9b94d9d7eadafb46f0fcb72051904e6f45d" + }, + { + "path": "src/shared/process-table-index.ts", + "sha256": "30f236b7d0275f05673c0a7982bb0aa67c9319762749f5a583c5c31e51e31f36" + }, + { + "path": "src/shared/process-table-snapshot.ts", + "sha256": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9" + }, + { + "path": "src/shared/process-table-snapshot-reader.ts", + "sha256": "7d35601a2a7bf7a9c5b4a897995e0e8a15775b4d35939f3dd1abe1a042bb1c5e" + }, + { + "path": "src/main/windows/windows-command-line-recovery-health.ts", + "sha256": "a135da3611d54fecb0dba52b4d81e06ca1532680424b55e7efa315b7c886ec33" + }, + { + "path": "src/shared/child-process/windows-system-binary.ts", + "sha256": "0c42c7c454534a0937f4153e9708aeb243bd930d130ffc6493ac42d21405606e" + }, + { + "path": "src/main/windows/windows-process-table-cim-scan.ts", + "sha256": "ecfa487a3139cc087eae78ff61bf358f7eb62aec3b20e9d064d96b6e37862ab0" + }, + { + "path": "src/main/windows/windows-process-table.ts", + "sha256": "de0565f724f49ca393bb55c1618e697ea34e98bfee132c196d0c3e97713aabe1" + }, + { + "path": "src/main/providers/windows-foreground-process-rows.ts", + "sha256": "374accddf905f5b60fb1be4010e8160bfaf7f1a3834ecaffbf0119e555c7a765" + }, + { + "path": "src/main/windows-pty-root-identity.ts", + "sha256": "5e1ec017199f1a78150064d22eca4fd724874473c042f78fa236f3831f761711" + }, + { + "path": "src/main/pty-descendant-termination.ts", + "sha256": "dbdfe91d9248b4fbfbdac1733237ff3a4776e596a6a127fbdf5c4fb1d987b61e" + }, + { + "path": "src/main/pty-descendant-exit-verification.ts", + "sha256": "1f7a14a8273a228180538b3d37a20e0631e60a2e497134de499907eabfeb9b77" + }, + { + "path": "src/main/runtime/agent-session-process-identity-probe.ts", + "sha256": "4f6bc0fc286f74b07a26bdfbe69a9124498deb289c5d96db8b1334e12fdcce33" + }, + { + "path": "src/main/codex/codex-structured-owner-identity.ts", + "sha256": "b5b286f638515cae36539d0061c3808e64e6b2ed464f9326255fcd2f655d85f7" + }, + { + "path": "src/main/runtime/agent-session-spawn-token-readback.ts", + "sha256": "1b2481a5487b9ee7e21a2103b468e69d91879f84a2a330b7fa81c7a3b056e399" + }, + { + "path": "src/main/runtime/agent-session-spawn-token-process-scan.ts", + "sha256": "57fe5ac196d53e266b4d5e9ea0fab7311a292b5f281cbe56eea98c5579634bfc" + }, + { + "path": "src/main/codex/codex-app-server-process-teardown.ts", + "sha256": "183110df0c529c8eaec77ed457a9b17c5322d313482119f488f019dbf6d73f75" + }, + { + "path": "src/main/codex/codex-app-server-frame-size-error.ts", + "sha256": "8f567def6e41edb29c3a56b2be4377f64a99999c76bf24be2980a296ce6c01e5" + }, + { + "path": "src/main/codex/codex-app-server-jsonl.ts", + "sha256": "428b802a6704ec57efb26c696ad9e77da727a990b881665fdf5f68542cf143b4" + }, + { + "path": "src/main/codex/codex-app-server-request-error.ts", + "sha256": "67140be11e2b29f93d8a352552af733d228ac1808b6cf53bb7227e3906794eea" + }, + { + "path": "src/main/codex/codex-app-server-record-prefix.ts", + "sha256": "e70f8492995568e2891af0f58a78e5eb9a2f5f1970b9a561aa1bd7af32673f0d" + }, + { + "path": "src/main/codex/codex-app-server-record-dispatch.ts", + "sha256": "48554c6f9d4cb056d1a8e2e495b1c3fdadecf891d8f02d9599a7af3989f6ab23" + }, + { + "path": "src/main/codex/codex-app-server-connection.ts", + "sha256": "30fcca9f4cda5d6cceb9215905d7f3ead2361764b80e0e67ba0bcbb55e00672d" + }, + { + "path": "src/main/codex/codex-structured-thread-facts.ts", + "sha256": "a244c847c5ca22c3e3fac3b075b51f0a566f8cf6726731f69f50f7eb079b063a" + }, + { + "path": "src/main/codex/codex-structured-turn-processes.ts", + "sha256": "6a942ed29995d8001f8c041c693ed7bd30ecfc3fac5dd30e4c5ec70c0a12396a" + }, + { + "path": "src/main/codex/codex-structured-turn-cancellation.ts", + "sha256": "518b8e5a2f6dc4f8e46e01366c0a61830d6c805b68101484726bd62bbfc831a8" + }, + { + "path": "src/shared/orca-dispatch-status-prompt.ts", + "sha256": "fd9fedb3c839cd9d17b545329937c8fb6b9fa081120eb4678a9fb66f4252629e" + }, + { + "path": "src/shared/agent-status-field-normalization.ts", + "sha256": "a60c119eade7026c9e6f37ff98e77e718bd0cb41e929b0e256872981411e3328" + }, + { + "path": "src/main/native-chat/agent-session-wire/provider-frame-activity.ts", + "sha256": "9888661f598cee4d622f22b208f9ba1fc6615df6424328cf818cebe9fe86b8d7" + }, + { + "path": "src/main/codex/codex-subagent-activity.ts", + "sha256": "5cdf18a4ea2c68178a67a845d4c95672a0d94bb282e8e15c3a930c6dd59151e4" + }, + { + "path": "src/shared/native-chat-types.ts", + "sha256": "ca9440aba61822a8b92cc1009790d5f6e7bdb2f570cf159349a42027dfd40def" + }, + { + "path": "src/shared/native-chat-subagent-summary.ts", + "sha256": "a2cdf19a07edf3b50a971af22044608d50ea9cfebd07abc01af38755a2143077" + }, + { + "path": "src/main/codex/codex-subagent-executions.ts", + "sha256": "2a356e9a108a302e1131d40112401a48c582b2d5560eab073004a7efefde5259" + }, + { + "path": "src/main/codex/codex-subagent-group-body.ts", + "sha256": "9ff41e055ee33fb556f2079604271f13e696ca0114eaaa9a5f25166e5fa49280" + }, + { + "path": "src/main/codex/codex-structured-journal-limits.ts", + "sha256": "2fd2d5015b73fb948df6abdb6469039b1ee3a680db5119b1a44ebf82bc152492" + }, + { + "path": "src/main/codex/codex-subagent-roster.ts", + "sha256": "bafd291665fb178b0912bd2a2723910c00890f0766aae7f70f8bed5d417542b1" + }, + { + "path": "src/shared/native-chat-turn-status.ts", + "sha256": "4ce2b64fa7818106814c135e5daf77bb183f601c35f9f08e5b7ff847fba01378" + }, + { + "path": "src/shared/native-chat-tool-identity.ts", + "sha256": "8c98eec1a53b26a67ed48a0859c5863f5accbe67e013c4eb5a0f8b32441ee925" + }, + { + "path": "src/main/codex/codex-goal-journal-rows.ts", + "sha256": "415846ca105300349778da7637216ca6ce978c7036219f6b3038f7228ba8724e" + }, + { + "path": "src/main/native-chat/agent-session-wire/provider-frame-disposition.ts", + "sha256": "f24008cebb1b8a77b0cbd091e49140a4bf99c676bc61eedeb0a82f1263244a7c" + }, + { + "path": "src/main/native-chat/agent-session-wire/unhandled-provider-frame.ts", + "sha256": "f8169a024a573adfc97e4eb8c99ecc4e80f82e9f94a04a29011a6b8051cc7626" + }, + { + "path": "src/shared/raster-image-dimensions.ts", + "sha256": "0d7462b8b2e53bfcdcbd32803edcfb85ed0063f56d45cf230fb59aac4783d063" + }, + { + "path": "src/shared/raster-image-preview-limits.ts", + "sha256": "f2d836b354b3951912f1ec689d84b82aa1aade89ed8e9c6f76be2321d600e9a7" + }, + { + "path": "src/shared/raster-image-base64-preview.ts", + "sha256": "89baf40f4637615456888e0f35115517a8445a3b1cb640b7bc495dd9a98fcedb" + }, + { + "path": "src/shared/image-data-uri.ts", + "sha256": "3eb9d2bda499b8f10c74783f9de766041469b140a9b96b2b5be7d97528688ce0" + }, + { + "path": "src/main/codex/codex-image-item-translation.ts", + "sha256": "c4f88236c707472cbcfcdfb1a04e51c738f8741aa5847a33f730759499522f5e" + }, + { + "path": "src/main/codex/codex-command-action-class.ts", + "sha256": "93b7bceb66a9e5bfae680e018b61bddb5c1117fe26243088ceccc8738841f966" + }, + { + "path": "src/main/codex/codex-thread-item-identity.ts", + "sha256": "04b98b367a4d658fb759ebc4fc73ea4ae731ae3f238f191c8ce002b2a2c9e2a8" + }, + { + "path": "src/main/codex/codex-turn-ordinals.ts", + "sha256": "9b7cf66986235bcacd6c198950452c7f40bd559a3a0ce20995aac496a1e32d7f" + }, + { + "path": "src/main/codex/codex-structured-item-translation.ts", + "sha256": "8ab9debb279baf3a5043ffe754c8911755c6e4ca9edfc0a094671c4d5d166639" + }, + { + "path": "src/main/codex/codex-structured-journal-contracts.ts", + "sha256": "59dbe0f4f38aea3ff31ae62d7a8d244636e2813bb4a0f1e1c2db3362bdf193ac" + }, + { + "path": "src/main/codex/codex-structured-journal-generic-frames.ts", + "sha256": "5fe2414993c44075d9c979d76091c89611360cb166f650e4483d1e6bad3b30d2" + }, + { + "path": "src/main/native-chat/agent-session-wire/structured-session-compaction.ts", + "sha256": "c73d272d59d71522d3d61eea66cf61da205c739743d720d15cb70f78bd5e07ac" + }, + { + "path": "src/shared/agent-session-journal-item-key.ts", + "sha256": "09ebe4758e3f38b5b7591f07f7b4dbfd04c7cc01713c9b0afe03b07b656d1fe8" + }, + { + "path": "src/shared/agent-session-journal-types.ts", + "sha256": "4f85b301aa12ea14ac87b0099135247ad0758529cdb2ce003bb6a1031cf279ea" + }, + { + "path": "src/shared/agent-session-journal-schemas.ts", + "sha256": "579ab7671b7f8e46374f11b6ac669bf659bd5505345c49d7dd1c9acc4b0ef564" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-row-schema.ts", + "sha256": "de9c4f6324feef96fd33cfb0698f4c34d7e02694809fb133757146ead0cbb9c3" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-lifecycle-batch-partition.ts", + "sha256": "9361c2255ac501b4c18baf22dd4e74936bc8c1181f227329b97bf1af7593bf4c" + }, + { + "path": "src/main/codex/codex-structured-journal-sink.ts", + "sha256": "e05226f2cb906557a0811de780b9f30e4bbedbd6253cd4948e5518646b63cf26" + }, + { + "path": "src/main/codex/codex-structured-journal-compactions.ts", + "sha256": "45c8e450be19cf8b88b5ffa90311e80390f40a32fd2ceaddf6bfd4cf7992ba03" + }, + { + "path": "src/main/codex/codex-goal-journal-identity.ts", + "sha256": "55d5c3a3120ea038aac9ae64cd51c5bfdf0e13af1306dca01009993cf482ad08" + }, + { + "path": "src/main/codex/codex-structured-journal-goals.ts", + "sha256": "d14794482031e86cf21f2c859ce8097f0759a5a0b2bc8f97ea26598ecfd190a5" + }, + { + "path": "src/shared/agent-turn-lifecycle-text.ts", + "sha256": "bf3a91d9c5157be19c9c88aed96e2c4e076e744362b34ddf2b466c707067e32a" + }, + { + "path": "src/shared/agent-session-turn-record.ts", + "sha256": "1a3df88a627b4744ccbe8168860033dfef66bd4ff028c6431af6a6ac2f63194f" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-terminal-settlement.ts", + "sha256": "9344b1593bf91a49a6e1d2fb9604e2890939eb1b1f0cadbbdae7194db57a8080" + }, + { + "path": "src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts", + "sha256": "48828e4ee21858075cbb87ec1caa4a82991a55f80928482615d1df7ec5dc0fb2" + }, + { + "path": "src/main/codex/codex-command-lifecycle.ts", + "sha256": "7b480d3b111304e3caff71768fb3b11ab6c36e1fa8f5b0440623a77cac2382c2" + }, + { + "path": "src/main/codex/codex-structured-item-stream-bounds.ts", + "sha256": "88e9efa8a9c163905f1a5eb5eaf316fba2f194d76c5a94ed7f55636cc0d74ae0" + }, + { + "path": "src/main/codex/codex-item-stream-retention.ts", + "sha256": "66d1f1d51ed6711e20f81698b14be2d003e899dbc587ac1306b6a656cf951b96" + }, + { + "path": "src/main/codex/codex-structured-item-stream-events.ts", + "sha256": "da2a9da9025af354ce705a190badadf822f9ac3b403eef04aa0ef8aaf6f023de" + }, + { + "path": "src/main/codex/codex-structured-item-streams.ts", + "sha256": "b70c3306e8bec3587342afc3bc156f40903513fa7ff3865b217ba4cc912cd89f" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-values.ts", + "sha256": "fb66d47e32f4040f1b248a886b077506d23d0492264412e6cc3c96b579de425d" + }, + { + "path": "src/main/codex/codex-structured-dispatch-echo.ts", + "sha256": "4be5e3b55bd0a5507878798f221e758444dc82ede2f14e741f08b6b85979cd7a" + }, + { + "path": "src/main/codex/codex-structured-journal-items.ts", + "sha256": "7a9dc981cdcb1931d620feaa5523b486600c50c241f223ffdc71aa9edeb4a5bb" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-prompt-body-bounds.ts", + "sha256": "e4382b9d5b8a9cdba94b2ee71188119790987b11fd0f8f9718ff4be899e9a9fa" + }, + { + "path": "src/main/codex/codex-structured-prompt-items.ts", + "sha256": "813e8fdf2768663e31822699a5a7311df8e714ca2a269dcbb1f42e8d196ac456" + }, + { + "path": "src/main/codex/codex-structured-journal-prompts.ts", + "sha256": "806f61f913c28593b02b6ea6dcb68559b126bf939b91b1e5cf4f43fedabf45a2" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-turns.ts", + "sha256": "9042b2393d7d8898ed4f7436669dfbc82504e15b9ea90eb00e1518ee4db254e0" + }, + { + "path": "src/main/codex/codex-structured-journal-settlement.ts", + "sha256": "761616c292e15894b337fdd171acd7c2c1272fcabdbad3ea460f8831a923c797" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-frames.ts", + "sha256": "0679aea6b6fc274448d007c292218f90bfdd8ad3ed4ce4a7d59d0a15aae9e455" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-restore.ts", + "sha256": "a2e521b7cae677a5b3fd671065e5e4b1c812e2444f272df0f79feddae3640652" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-turn-boundaries.ts", + "sha256": "bb482b2c49fa3754b76e1e749185bb2804409e017f909c0453022ead07f99a03" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-turn-state.ts", + "sha256": "f6dd637f0e77f193923c3f3843b86d12786b75b9ce76e5a7e057a7187a832fe5" + }, + { + "path": "src/main/codex/codex-structured-journal-translation.ts", + "sha256": "56e17b9649471e804d29d1d54a3e14b4298ca8af2e25c4759dc37fce000b06a1" + }, + { + "path": "src/main/codex/codex-server-request-disposition.ts", + "sha256": "ddd362e00a38ff0166d35bc4cca51e4f79e377f188a9e07f5966da7c056854a0" + }, + { + "path": "src/shared/node-bounded-json-stringify.ts", + "sha256": "fcadabc537aa3125d1ee514026ecca5b8e2a76aabca7e29f08d3d35008001622" + }, + { + "path": "src/shared/remote-runtime-client-error.ts", + "sha256": "593ff4ee6466c4b18b69f65a20631967205fb8e1bd5e46239cf7f526b764156a" + }, + { + "path": "src/shared/remote-runtime-memory-limits.ts", + "sha256": "862a317d7f564a2cc54cfa60b6c093de5dc8a61714eac45daa178608c1f6acb1" + }, + { + "path": "src/main/native-chat/agent-session-wire/agent-session-history-page-bounds.ts", + "sha256": "57e49a0ea59cdccf4a4fc1532d43d2c127b077233602139b2d36d79fd547fc9d" + }, + { + "path": "src/main/codex/codex-structured-rewind.ts", + "sha256": "710115f53bc673f7d7259ec9e1d6b02e7827c348225c1e6a2006ed5eaf06e6bc" + }, + { + "path": "src/main/codex/codex-structured-provider-events.ts", + "sha256": "a411dc378e521ea8fc915e2a2a21476341a30f440e8233ae5649205d2a1e2e44" + } + ] + }, + "candidate": { + "bundleSha256": "6b7ec91709cad63ae089187e297914243757d59991842be0c0c2eb92514b3548", + "dependencies": [ + { + "path": "src/main/native-chat/agent-session-journal/journal-payload-bounds.ts", + "sha256": "a421da6c6eee9346746a69f8ba7ff966ce81969dfa0a93f58784cac27cf8cdad" + }, + { + "path": "src/shared/agent-session-wire-refusals.ts", + "sha256": "25123cce70418deb3d2f75cf5c3b7431aa6a8c3b834f022cd697230345c595ea" + }, + { + "path": "src/shared/agent-session-background-task-wire.ts", + "sha256": "6a756544bb07864d5a6e4573992f4b3bb0d0788acb686c16da177095be23167c" + }, + { + "path": "src/shared/agent-session-wire.ts", + "sha256": "466b641465f92957dafe873c68d4cb42f50aeb0768a111397050b50aa96ac64f" + }, + { + "path": "src/main/codex/codex-prompt-registry-bounds.ts", + "sha256": "f6bfd21bcc2ccf7005a93a4764d17235887f11380b0b29301410b0f291a5c618" + }, + { + "path": "src/main/codex/codex-item-field-readers.ts", + "sha256": "aa4b178c562a63995b7de63c6f5daaf7b6b610fb163fb0a357cc230314076323" + }, + { + "path": "src/main/codex/codex-prompt-registry.ts", + "sha256": "3d33f98215b9547d661c0a4f6aa8b9a8a6a88fa953d90652cb3fb9953425a539" + }, + { + "path": "src/main/native-chat/agent-session-wire/structured-agent-session-adapter.ts", + "sha256": "9a7bb24e419dbd41f4aaaa20b87dd82859579d663ebb800fc605df4b471749ef" + }, + { + "path": "src/main/codex/codex-structured-prompt-replies.ts", + "sha256": "7b927e9910031ee80ab885ce1d713133d3ee614c5b6d460a5c91ff3768d9cb2e" + }, + { + "path": "src/shared/child-process/cancel-process-acquisition.ts", + "sha256": "709a04316da5f3528e33e02bbb6f054012714efc73628a04fd08289eae0c42d5" + }, + { + "path": "src/main/codex/codex-structured-acquisition-window.ts", + "sha256": "6828970d450a53fcb30ecc0ecb20d8452395bf579cc2295ee53415c299d039a4" + }, + { + "path": "src/main/codex/codex-structured-session-state.ts", + "sha256": "271837e87efac55238342a26ccdf350d9eb663b1d38a2553886429d1376ab1f4" + }, + { + "path": "src/main/codex/codex-structured-prompt-ownership.ts", + "sha256": "8d86f3784b79a1470e4002527bdfa505efe2762cba865de7716442f7d9730013" + }, + { + "path": "src/shared/child-process/windows-command-line.ts", + "sha256": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca" + }, + { + "path": "src/shared/child-process/windows-cmd-shim-resolution.ts", + "sha256": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3" + }, + { + "path": "src/shared/child-process/spawn-resolution.ts", + "sha256": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc" + }, + { + "path": "src/shared/child-process/process-tree-kill-gate.ts", + "sha256": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab" + }, + { + "path": "src/shared/child-process/process-tree-termination.ts", + "sha256": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120" + }, + { + "path": "src/shared/child-process/bounded-output-sink.ts", + "sha256": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865" + }, + { + "path": "src/shared/child-process/child-termination-reporter.ts", + "sha256": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0" + }, + { + "path": "src/shared/child-process/process-spec.ts", + "sha256": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289" + }, + { + "path": "src/shared/child-process/run-process.ts", + "sha256": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad" + }, + { + "path": "src/shared/child-process/retryable-process-exit-proof.ts", + "sha256": "411a39e4508763d8c4ef331bd16e88c1fe497f03188493241afcce6eab0a6e65" + }, + { + "path": "src/main/codex/codex-app-server-posix-supervisor.ts", + "sha256": "ccd4ff7a43d3ba92b9becab45304d5f5522f5c1d5aa53d379fdd47845d0fe545" + }, + { + "path": "src/main/codex/codex-app-server-capability-signal.ts", + "sha256": "43cf352901aeff4ed9143f51f224770f3dc92bf79dcfac6875bbc64105184c2f" + }, + { + "path": "src/main/codex/codex-process-exit-deadline.ts", + "sha256": "952cbb7c8778b8187ff443f8c045be53c14c3778af1b2ec77a3af5cd3ff13364" + }, + { + "path": "src/shared/system-cli-install-dirs.ts", + "sha256": "635b59f51022c9f31bf7720385b343018d4ce849220d422115e74f5351a9f21d" + }, + { + "path": "src/shared/node-cli-command-resolution.ts", + "sha256": "5eecee0c0d6296b962315c2ef2ae228a1bb1b6a7449f46d5afe3977f0af1e145" + }, + { + "path": "src/main/codex/codex-app-server-process-tree-kill.ts", + "sha256": "0a370b1d40509aa967634d6551f6baf5462b648910ddb79c2624555a84116b0b" + }, + { + "path": "src/shared/main-process-ndjson-framer.ts", + "sha256": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490" + }, + { + "path": "src/main/codex/codex-app-server-record-reader.ts", + "sha256": "b4d600293228ae2429090facc3db6f98dff449f2c3d82f35a0bea11fe4d70048" + }, + { + "path": "src/main/codex/codex-app-server-session.ts", + "sha256": "9adfbab8e229d3998d973ad9abfe647015ff21a5517fa6e5c7b32acf0a446eb8" + }, + { + "path": "src/main/codex/codex-app-server-exit-error.ts", + "sha256": "d58cdd97ae3ad4a786c7c4a0b1062ee3c5bb15b0943046dda7fff4b51ede21d1" + }, + { + "path": "src/main/codex/codex-app-server-handshake.ts", + "sha256": "3dc86d5961076713cb67ff58f593ca0fe848cd2f6b5a3a0a5da83773f49e6516" + }, + { + "path": "src/main/codex/codex-app-server-handshake-exit-proof.ts", + "sha256": "01832f6ab97bb68566221151d6a88e03cd5461f699991ad0e2c1f52573541ca1" + }, + { + "path": "src/shared/crash-reporting-diagnostic-bundle.ts", + "sha256": "2ac76a8941d4e8ae8d4e0a95ac9e10102602670e4c8fdbcb88a03b014903d1ba" + }, + { + "path": "src/shared/crash-report-signature-lines.ts", + "sha256": "564936f235f0c9faefc0c9f18f2fcbc588e5bef69ecd1071baac5a30a7c662a8" + }, + { + "path": "src/shared/posix-wait-status.ts", + "sha256": "1eb72525881baa2815548d31d2b83aaecf88e5dfd0258dbcaa5cda1e8fdcd69f" + }, + { + "path": "src/shared/crash-report-exit-code.ts", + "sha256": "a73f4e6fce72f11f5eec32c2a22d2e749f330f434aef20cec6dc03dbde55ebc6" + }, + { + "path": "src/shared/react-update-depth-attribution.ts", + "sha256": "15fd538fa4953088b63d9307fc575d4f795614b445a558340d80d61bb4c50929" + }, + { + "path": "src/shared/crash-report-attribution-lines.ts", + "sha256": "3db6cabca0444343347a90e6ec71a015a79489097e2484202cf2b57232483a6e" + }, + { + "path": "src/shared/crash-report-redaction.ts", + "sha256": "afd00bd49c77398425ee481070818e98067e90bb472a7454c571bf77ceee9c7f" + }, + { + "path": "src/shared/crash-reporting.ts", + "sha256": "e9749318038d145b7ae1819ffed6b285815c502d0eba13181c48b73b1bd3e918" + }, + { + "path": "src/main/observability/redactor.ts", + "sha256": "59190309dbef22508e3f2ef27b0190093d5aaab8cce146d6d49a89eb9d0a2b73" + }, + { + "path": "src/main/observability/tracer.ts", + "sha256": "82b6f4ec0be07b6aa612038b21b9f67b0ef199f694d29dc9f7b54b55dba9776e" + }, + { + "path": "src/main/crash-reporting/crash-breadcrumb-store.ts", + "sha256": "8848be8a897e0d1a214dbbaf87a0377d493ee83358763c0fc286e693889b2cb9" + }, + { + "path": "src/main/crash-reporting/main-process-lifecycle-identity.ts", + "sha256": "797cd37e5af6ec71f91636bc779274572a62ea67e5fa2874bb1f2f771020c950" + }, + { + "path": "src/main/crash-reporting/durable-crash-breadcrumb.ts", + "sha256": "477e28157e119f3bd85bc77d4adb185d36e44103b8a087d680715ce71b05dea7" + }, + { + "path": "src/main/crash-reporting/self-initiated-tree-kill-log.ts", + "sha256": "eb28b4a513a3cbc30586df30569adf29d828f059522ebe6c751d55b233c13987" + }, + { + "path": "src/shared/app-environment.ts", + "sha256": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140" + }, + { + "path": "src/main/orca-chromium-process-pids.ts", + "sha256": "960a15f005ff997fa379972a8c2abe3410b78c6facee449e8a5350754cfce965" + }, + { + "path": "src/main/own-chromium-tree-kill-guard.ts", + "sha256": "3a59cccd8cf923506aacad58f1c5d11b49f47a21e6c6dee8559912ee3c5cefb5" + }, + { + "path": "src/main/windows-process-tree-kill.ts", + "sha256": "83236378e93e3a15189ca02febf6b9b94d9d7eadafb46f0fcb72051904e6f45d" + }, + { + "path": "src/shared/process-table-index.ts", + "sha256": "30f236b7d0275f05673c0a7982bb0aa67c9319762749f5a583c5c31e51e31f36" + }, + { + "path": "src/shared/process-table-snapshot.ts", + "sha256": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9" + }, + { + "path": "src/shared/process-table-snapshot-reader.ts", + "sha256": "7d35601a2a7bf7a9c5b4a897995e0e8a15775b4d35939f3dd1abe1a042bb1c5e" + }, + { + "path": "src/main/windows/windows-command-line-recovery-health.ts", + "sha256": "a135da3611d54fecb0dba52b4d81e06ca1532680424b55e7efa315b7c886ec33" + }, + { + "path": "src/shared/child-process/windows-system-binary.ts", + "sha256": "0c42c7c454534a0937f4153e9708aeb243bd930d130ffc6493ac42d21405606e" + }, + { + "path": "src/main/windows/windows-process-table-cim-scan.ts", + "sha256": "ecfa487a3139cc087eae78ff61bf358f7eb62aec3b20e9d064d96b6e37862ab0" + }, + { + "path": "src/main/windows/windows-process-table.ts", + "sha256": "de0565f724f49ca393bb55c1618e697ea34e98bfee132c196d0c3e97713aabe1" + }, + { + "path": "src/main/providers/windows-foreground-process-rows.ts", + "sha256": "374accddf905f5b60fb1be4010e8160bfaf7f1a3834ecaffbf0119e555c7a765" + }, + { + "path": "src/main/windows-pty-root-identity.ts", + "sha256": "5e1ec017199f1a78150064d22eca4fd724874473c042f78fa236f3831f761711" + }, + { + "path": "src/main/pty-descendant-termination.ts", + "sha256": "dbdfe91d9248b4fbfbdac1733237ff3a4776e596a6a127fbdf5c4fb1d987b61e" + }, + { + "path": "src/main/pty-descendant-exit-verification.ts", + "sha256": "1f7a14a8273a228180538b3d37a20e0631e60a2e497134de499907eabfeb9b77" + }, + { + "path": "src/main/runtime/agent-session-process-identity-probe.ts", + "sha256": "4f6bc0fc286f74b07a26bdfbe69a9124498deb289c5d96db8b1334e12fdcce33" + }, + { + "path": "src/main/codex/codex-structured-owner-identity.ts", + "sha256": "b5b286f638515cae36539d0061c3808e64e6b2ed464f9326255fcd2f655d85f7" + }, + { + "path": "src/main/runtime/agent-session-spawn-token-readback.ts", + "sha256": "1b2481a5487b9ee7e21a2103b468e69d91879f84a2a330b7fa81c7a3b056e399" + }, + { + "path": "src/main/runtime/agent-session-spawn-token-process-scan.ts", + "sha256": "57fe5ac196d53e266b4d5e9ea0fab7311a292b5f281cbe56eea98c5579634bfc" + }, + { + "path": "src/main/codex/codex-app-server-process-teardown.ts", + "sha256": "183110df0c529c8eaec77ed457a9b17c5322d313482119f488f019dbf6d73f75" + }, + { + "path": "src/main/codex/codex-app-server-frame-size-error.ts", + "sha256": "8f567def6e41edb29c3a56b2be4377f64a99999c76bf24be2980a296ce6c01e5" + }, + { + "path": "src/main/codex/codex-app-server-jsonl.ts", + "sha256": "428b802a6704ec57efb26c696ad9e77da727a990b881665fdf5f68542cf143b4" + }, + { + "path": "src/main/codex/codex-app-server-request-error.ts", + "sha256": "67140be11e2b29f93d8a352552af733d228ac1808b6cf53bb7227e3906794eea" + }, + { + "path": "src/main/codex/codex-app-server-record-prefix.ts", + "sha256": "e70f8492995568e2891af0f58a78e5eb9a2f5f1970b9a561aa1bd7af32673f0d" + }, + { + "path": "src/main/codex/codex-app-server-record-dispatch.ts", + "sha256": "48554c6f9d4cb056d1a8e2e495b1c3fdadecf891d8f02d9599a7af3989f6ab23" + }, + { + "path": "src/main/codex/codex-app-server-connection.ts", + "sha256": "30fcca9f4cda5d6cceb9215905d7f3ead2361764b80e0e67ba0bcbb55e00672d" + }, + { + "path": "src/main/codex/codex-structured-thread-facts.ts", + "sha256": "a244c847c5ca22c3e3fac3b075b51f0a566f8cf6726731f69f50f7eb079b063a" + }, + { + "path": "src/main/codex/codex-structured-turn-processes.ts", + "sha256": "6a942ed29995d8001f8c041c693ed7bd30ecfc3fac5dd30e4c5ec70c0a12396a" + }, + { + "path": "src/main/codex/codex-structured-turn-cancellation.ts", + "sha256": "518b8e5a2f6dc4f8e46e01366c0a61830d6c805b68101484726bd62bbfc831a8" + }, + { + "path": "src/shared/orca-dispatch-status-prompt.ts", + "sha256": "fd9fedb3c839cd9d17b545329937c8fb6b9fa081120eb4678a9fb66f4252629e" + }, + { + "path": "src/shared/agent-status-field-normalization.ts", + "sha256": "a60c119eade7026c9e6f37ff98e77e718bd0cb41e929b0e256872981411e3328" + }, + { + "path": "src/main/native-chat/agent-session-wire/provider-frame-activity.ts", + "sha256": "9888661f598cee4d622f22b208f9ba1fc6615df6424328cf818cebe9fe86b8d7" + }, + { + "path": "src/main/codex/codex-subagent-activity.ts", + "sha256": "5cdf18a4ea2c68178a67a845d4c95672a0d94bb282e8e15c3a930c6dd59151e4" + }, + { + "path": "src/shared/native-chat-types.ts", + "sha256": "ca9440aba61822a8b92cc1009790d5f6e7bdb2f570cf159349a42027dfd40def" + }, + { + "path": "src/shared/native-chat-subagent-summary.ts", + "sha256": "a2cdf19a07edf3b50a971af22044608d50ea9cfebd07abc01af38755a2143077" + }, + { + "path": "src/main/codex/codex-subagent-executions.ts", + "sha256": "2a356e9a108a302e1131d40112401a48c582b2d5560eab073004a7efefde5259" + }, + { + "path": "src/main/codex/codex-subagent-group-body.ts", + "sha256": "9ff41e055ee33fb556f2079604271f13e696ca0114eaaa9a5f25166e5fa49280" + }, + { + "path": "src/main/codex/codex-structured-journal-limits.ts", + "sha256": "2fd2d5015b73fb948df6abdb6469039b1ee3a680db5119b1a44ebf82bc152492" + }, + { + "path": "src/main/codex/codex-subagent-roster.ts", + "sha256": "bafd291665fb178b0912bd2a2723910c00890f0766aae7f70f8bed5d417542b1" + }, + { + "path": "src/shared/native-chat-turn-status.ts", + "sha256": "4ce2b64fa7818106814c135e5daf77bb183f601c35f9f08e5b7ff847fba01378" + }, + { + "path": "src/shared/native-chat-tool-identity.ts", + "sha256": "8c98eec1a53b26a67ed48a0859c5863f5accbe67e013c4eb5a0f8b32441ee925" + }, + { + "path": "src/main/codex/codex-goal-journal-rows.ts", + "sha256": "415846ca105300349778da7637216ca6ce978c7036219f6b3038f7228ba8724e" + }, + { + "path": "src/main/native-chat/agent-session-wire/provider-frame-disposition.ts", + "sha256": "f24008cebb1b8a77b0cbd091e49140a4bf99c676bc61eedeb0a82f1263244a7c" + }, + { + "path": "src/main/native-chat/agent-session-wire/unhandled-provider-frame.ts", + "sha256": "f8169a024a573adfc97e4eb8c99ecc4e80f82e9f94a04a29011a6b8051cc7626" + }, + { + "path": "src/shared/raster-image-dimensions.ts", + "sha256": "0d7462b8b2e53bfcdcbd32803edcfb85ed0063f56d45cf230fb59aac4783d063" + }, + { + "path": "src/shared/raster-image-preview-limits.ts", + "sha256": "f2d836b354b3951912f1ec689d84b82aa1aade89ed8e9c6f76be2321d600e9a7" + }, + { + "path": "src/shared/raster-image-base64-preview.ts", + "sha256": "89baf40f4637615456888e0f35115517a8445a3b1cb640b7bc495dd9a98fcedb" + }, + { + "path": "src/shared/image-data-uri.ts", + "sha256": "3eb9d2bda499b8f10c74783f9de766041469b140a9b96b2b5be7d97528688ce0" + }, + { + "path": "src/main/codex/codex-image-item-translation.ts", + "sha256": "c4f88236c707472cbcfcdfb1a04e51c738f8741aa5847a33f730759499522f5e" + }, + { + "path": "src/main/codex/codex-command-action-class.ts", + "sha256": "93b7bceb66a9e5bfae680e018b61bddb5c1117fe26243088ceccc8738841f966" + }, + { + "path": "src/main/codex/codex-thread-item-identity.ts", + "sha256": "04b98b367a4d658fb759ebc4fc73ea4ae731ae3f238f191c8ce002b2a2c9e2a8" + }, + { + "path": "src/main/codex/codex-turn-ordinals.ts", + "sha256": "9b7cf66986235bcacd6c198950452c7f40bd559a3a0ce20995aac496a1e32d7f" + }, + { + "path": "src/main/codex/codex-structured-item-translation.ts", + "sha256": "8ab9debb279baf3a5043ffe754c8911755c6e4ca9edfc0a094671c4d5d166639" + }, + { + "path": "src/main/codex/codex-structured-journal-contracts.ts", + "sha256": "59dbe0f4f38aea3ff31ae62d7a8d244636e2813bb4a0f1e1c2db3362bdf193ac" + }, + { + "path": "src/main/codex/codex-structured-journal-generic-frames.ts", + "sha256": "5fe2414993c44075d9c979d76091c89611360cb166f650e4483d1e6bad3b30d2" + }, + { + "path": "src/main/native-chat/agent-session-wire/structured-session-compaction.ts", + "sha256": "c73d272d59d71522d3d61eea66cf61da205c739743d720d15cb70f78bd5e07ac" + }, + { + "path": "src/shared/agent-session-journal-item-key.ts", + "sha256": "09ebe4758e3f38b5b7591f07f7b4dbfd04c7cc01713c9b0afe03b07b656d1fe8" + }, + { + "path": "src/shared/agent-session-journal-types.ts", + "sha256": "4f85b301aa12ea14ac87b0099135247ad0758529cdb2ce003bb6a1031cf279ea" + }, + { + "path": "src/shared/agent-session-journal-schemas.ts", + "sha256": "579ab7671b7f8e46374f11b6ac669bf659bd5505345c49d7dd1c9acc4b0ef564" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-row-schema.ts", + "sha256": "de9c4f6324feef96fd33cfb0698f4c34d7e02694809fb133757146ead0cbb9c3" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-lifecycle-batch-partition.ts", + "sha256": "9361c2255ac501b4c18baf22dd4e74936bc8c1181f227329b97bf1af7593bf4c" + }, + { + "path": "src/main/codex/codex-structured-journal-sink.ts", + "sha256": "e05226f2cb906557a0811de780b9f30e4bbedbd6253cd4948e5518646b63cf26" + }, + { + "path": "src/main/codex/codex-structured-journal-compactions.ts", + "sha256": "45c8e450be19cf8b88b5ffa90311e80390f40a32fd2ceaddf6bfd4cf7992ba03" + }, + { + "path": "src/main/codex/codex-goal-journal-identity.ts", + "sha256": "55d5c3a3120ea038aac9ae64cd51c5bfdf0e13af1306dca01009993cf482ad08" + }, + { + "path": "src/main/codex/codex-structured-journal-goals.ts", + "sha256": "d14794482031e86cf21f2c859ce8097f0759a5a0b2bc8f97ea26598ecfd190a5" + }, + { + "path": "src/shared/agent-turn-lifecycle-text.ts", + "sha256": "bf3a91d9c5157be19c9c88aed96e2c4e076e744362b34ddf2b466c707067e32a" + }, + { + "path": "src/shared/agent-session-turn-record.ts", + "sha256": "1a3df88a627b4744ccbe8168860033dfef66bd4ff028c6431af6a6ac2f63194f" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-terminal-settlement.ts", + "sha256": "9344b1593bf91a49a6e1d2fb9604e2890939eb1b1f0cadbbdae7194db57a8080" + }, + { + "path": "src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts", + "sha256": "48828e4ee21858075cbb87ec1caa4a82991a55f80928482615d1df7ec5dc0fb2" + }, + { + "path": "src/main/codex/codex-command-lifecycle.ts", + "sha256": "7b480d3b111304e3caff71768fb3b11ab6c36e1fa8f5b0440623a77cac2382c2" + }, + { + "path": "src/main/codex/codex-structured-item-stream-bounds.ts", + "sha256": "88e9efa8a9c163905f1a5eb5eaf316fba2f194d76c5a94ed7f55636cc0d74ae0" + }, + { + "path": "src/main/codex/codex-item-stream-retention.ts", + "sha256": "66d1f1d51ed6711e20f81698b14be2d003e899dbc587ac1306b6a656cf951b96" + }, + { + "path": "src/main/codex/codex-structured-item-stream-events.ts", + "sha256": "da2a9da9025af354ce705a190badadf822f9ac3b403eef04aa0ef8aaf6f023de" + }, + { + "path": "src/main/codex/codex-structured-item-streams.ts", + "sha256": "b70c3306e8bec3587342afc3bc156f40903513fa7ff3865b217ba4cc912cd89f" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-values.ts", + "sha256": "fb66d47e32f4040f1b248a886b077506d23d0492264412e6cc3c96b579de425d" + }, + { + "path": "src/main/codex/codex-structured-dispatch-echo.ts", + "sha256": "4be5e3b55bd0a5507878798f221e758444dc82ede2f14e741f08b6b85979cd7a" + }, + { + "path": "src/main/codex/codex-structured-journal-items.ts", + "sha256": "7a9dc981cdcb1931d620feaa5523b486600c50c241f223ffdc71aa9edeb4a5bb" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-prompt-body-bounds.ts", + "sha256": "e4382b9d5b8a9cdba94b2ee71188119790987b11fd0f8f9718ff4be899e9a9fa" + }, + { + "path": "src/main/codex/codex-structured-prompt-items.ts", + "sha256": "813e8fdf2768663e31822699a5a7311df8e714ca2a269dcbb1f42e8d196ac456" + }, + { + "path": "src/main/codex/codex-structured-journal-prompts.ts", + "sha256": "806f61f913c28593b02b6ea6dcb68559b126bf939b91b1e5cf4f43fedabf45a2" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-turns.ts", + "sha256": "9042b2393d7d8898ed4f7436669dfbc82504e15b9ea90eb00e1518ee4db254e0" + }, + { + "path": "src/main/codex/codex-structured-journal-settlement.ts", + "sha256": "761616c292e15894b337fdd171acd7c2c1272fcabdbad3ea460f8831a923c797" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-frames.ts", + "sha256": "0679aea6b6fc274448d007c292218f90bfdd8ad3ed4ce4a7d59d0a15aae9e455" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-restore.ts", + "sha256": "a2e521b7cae677a5b3fd671065e5e4b1c812e2444f272df0f79feddae3640652" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-turn-boundaries.ts", + "sha256": "bb482b2c49fa3754b76e1e749185bb2804409e017f909c0453022ead07f99a03" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-turn-state.ts", + "sha256": "f6dd637f0e77f193923c3f3843b86d12786b75b9ce76e5a7e057a7187a832fe5" + }, + { + "path": "src/main/codex/codex-structured-journal-translation.ts", + "sha256": "56e17b9649471e804d29d1d54a3e14b4298ca8af2e25c4759dc37fce000b06a1" + }, + { + "path": "src/main/codex/codex-server-request-disposition.ts", + "sha256": "ddd362e00a38ff0166d35bc4cca51e4f79e377f188a9e07f5966da7c056854a0" + }, + { + "path": "src/shared/node-bounded-json-stringify.ts", + "sha256": "fcadabc537aa3125d1ee514026ecca5b8e2a76aabca7e29f08d3d35008001622" + }, + { + "path": "src/shared/remote-runtime-client-error.ts", + "sha256": "593ff4ee6466c4b18b69f65a20631967205fb8e1bd5e46239cf7f526b764156a" + }, + { + "path": "src/shared/remote-runtime-memory-limits.ts", + "sha256": "862a317d7f564a2cc54cfa60b6c093de5dc8a61714eac45daa178608c1f6acb1" + }, + { + "path": "src/main/native-chat/agent-session-wire/agent-session-history-page-bounds.ts", + "sha256": "57e49a0ea59cdccf4a4fc1532d43d2c127b077233602139b2d36d79fd547fc9d" + }, + { + "path": "src/main/codex/codex-structured-rewind.ts", + "sha256": "710115f53bc673f7d7259ec9e1d6b02e7827c348225c1e6a2006ed5eaf06e6bc" + }, + { + "path": "src/main/codex/codex-structured-provider-events.ts", + "sha256": "a411dc378e521ea8fc915e2a2a21476341a30f440e8233ae5649205d2a1e2e44" + } + ] + } + } +} diff --git a/docs/audits/codex-prompt-claim-retention/fix.patch b/docs/audits/codex-prompt-claim-retention/fix.patch new file mode 100644 index 00000000000..9c424fe1b10 --- /dev/null +++ b/docs/audits/codex-prompt-claim-retention/fix.patch @@ -0,0 +1,11 @@ +--- a/src/main/codex/codex-prompt-registry.ts ++++ b/src/main/codex/codex-prompt-registry.ts +@@ -226,7 +226,7 @@ + + clearTurn(threadId: string, turnId: string): void { + const prompts = new Set( +- [...this.byAddress.values(), ...this.boundPrompts.values()].filter( ++ [...this.byAddress.values(), ...this.boundPrompts.values(), ...this.claims.keys()].filter( + (prompt) => prompt.threadId === threadId && codexPromptMatchesTurn(prompt, turnId) + ) + ) diff --git a/docs/audits/codex-prompt-claim-retention/node-results.json b/docs/audits/codex-prompt-claim-retention/node-results.json new file mode 100644 index 00000000000..60b11e1be5a --- /dev/null +++ b/docs/audits/codex-prompt-claim-retention/node-results.json @@ -0,0 +1,1238 @@ +{ + "capturedAt": "2026-09-17T02:39:39.462Z", + "runtime": { + "node": "26.6.0", + "acorn": "8.17.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.3", + "libffi": "3.7.1", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "147", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "3.6.3", + "simdjson": "4.6.6", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2026a", + "undici": "8.9.0", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "14.6.202.34-node.26", + "zlib": "1.2.12", + "zstd": "1.5.7" + }, + "scope": "Actual registry, server-request translation, cancellation ownership/cancellation class, journal translator, delayed turn completion. Injected accepted sink, interrupt transport, and compaction lookup. No provider, process enumeration/termination, or host data. Bundles load in memory; only the requested report is written.", + "sourceHashes": { + "src/main/codex/codex-prompt-registry.ts": { + "before": "254c530216f47abfc3572bb1689161ad3c10775af2cdceda7e94f443e52c0691", + "after": "3d33f98215b9547d661c0a4f6aa8b9a8a6a88fa953d90652cb3fb9953425a539" + } + }, + "countsOnly": true, + "noPayloadAmplification": true, + "results": { + "original": { + "ordinaryAfterCompletion": 0, + "cancelledPrompts": 32, + "sizesAfterEviction": { + "prompts": 128, + "journalBindings": 256 + }, + "afterEvictionBeforeCompletion": 32, + "afterExactTurnCompletion": 32, + "afterAllLookupMapsEmpty": 32, + "afterSessionClear": 0, + "oldAfterReplacementCompletion": 1, + "replacementClaimPreserved": true, + "wrongThreadPreserved": true, + "wrongTurnPreserved": true, + "rejectedCompletionPreserved": true, + "successfulInterruptRequests": 34 + }, + "candidate": { + "ordinaryAfterCompletion": 0, + "cancelledPrompts": 32, + "sizesAfterEviction": { + "prompts": 128, + "journalBindings": 256 + }, + "afterEvictionBeforeCompletion": 32, + "afterExactTurnCompletion": 0, + "afterAllLookupMapsEmpty": 0, + "afterSessionClear": 0, + "oldAfterReplacementCompletion": 0, + "replacementClaimPreserved": true, + "wrongThreadPreserved": true, + "wrongTurnPreserved": true, + "rejectedCompletionPreserved": true, + "successfulInterruptRequests": 34 + } + }, + "versions": { + "original": { + "bundleSha256": "e060beb8d88044d6abe07134880d9ddcbb8487464c963d0cce336454e789bcc8", + "dependencies": [ + { + "path": "src/main/native-chat/agent-session-journal/journal-payload-bounds.ts", + "sha256": "a421da6c6eee9346746a69f8ba7ff966ce81969dfa0a93f58784cac27cf8cdad" + }, + { + "path": "src/shared/agent-session-wire-refusals.ts", + "sha256": "25123cce70418deb3d2f75cf5c3b7431aa6a8c3b834f022cd697230345c595ea" + }, + { + "path": "src/shared/agent-session-background-task-wire.ts", + "sha256": "6a756544bb07864d5a6e4573992f4b3bb0d0788acb686c16da177095be23167c" + }, + { + "path": "src/shared/agent-session-wire.ts", + "sha256": "466b641465f92957dafe873c68d4cb42f50aeb0768a111397050b50aa96ac64f" + }, + { + "path": "src/main/codex/codex-prompt-registry-bounds.ts", + "sha256": "f6bfd21bcc2ccf7005a93a4764d17235887f11380b0b29301410b0f291a5c618" + }, + { + "path": "src/main/codex/codex-item-field-readers.ts", + "sha256": "aa4b178c562a63995b7de63c6f5daaf7b6b610fb163fb0a357cc230314076323" + }, + { + "path": "src/main/codex/codex-prompt-registry.ts", + "sha256": "254c530216f47abfc3572bb1689161ad3c10775af2cdceda7e94f443e52c0691" + }, + { + "path": "src/main/native-chat/agent-session-wire/structured-agent-session-adapter.ts", + "sha256": "9a7bb24e419dbd41f4aaaa20b87dd82859579d663ebb800fc605df4b471749ef" + }, + { + "path": "src/main/codex/codex-structured-prompt-replies.ts", + "sha256": "7b927e9910031ee80ab885ce1d713133d3ee614c5b6d460a5c91ff3768d9cb2e" + }, + { + "path": "src/shared/child-process/cancel-process-acquisition.ts", + "sha256": "709a04316da5f3528e33e02bbb6f054012714efc73628a04fd08289eae0c42d5" + }, + { + "path": "src/main/codex/codex-structured-acquisition-window.ts", + "sha256": "6828970d450a53fcb30ecc0ecb20d8452395bf579cc2295ee53415c299d039a4" + }, + { + "path": "src/main/codex/codex-structured-session-state.ts", + "sha256": "271837e87efac55238342a26ccdf350d9eb663b1d38a2553886429d1376ab1f4" + }, + { + "path": "src/main/codex/codex-structured-prompt-ownership.ts", + "sha256": "8d86f3784b79a1470e4002527bdfa505efe2762cba865de7716442f7d9730013" + }, + { + "path": "src/shared/child-process/windows-command-line.ts", + "sha256": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca" + }, + { + "path": "src/shared/child-process/windows-cmd-shim-resolution.ts", + "sha256": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3" + }, + { + "path": "src/shared/child-process/spawn-resolution.ts", + "sha256": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc" + }, + { + "path": "src/shared/child-process/process-tree-kill-gate.ts", + "sha256": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab" + }, + { + "path": "src/shared/child-process/process-tree-termination.ts", + "sha256": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120" + }, + { + "path": "src/shared/child-process/bounded-output-sink.ts", + "sha256": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865" + }, + { + "path": "src/shared/child-process/child-termination-reporter.ts", + "sha256": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0" + }, + { + "path": "src/shared/child-process/process-spec.ts", + "sha256": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289" + }, + { + "path": "src/shared/child-process/run-process.ts", + "sha256": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad" + }, + { + "path": "src/shared/child-process/retryable-process-exit-proof.ts", + "sha256": "411a39e4508763d8c4ef331bd16e88c1fe497f03188493241afcce6eab0a6e65" + }, + { + "path": "src/main/codex/codex-app-server-posix-supervisor.ts", + "sha256": "ccd4ff7a43d3ba92b9becab45304d5f5522f5c1d5aa53d379fdd47845d0fe545" + }, + { + "path": "src/main/codex/codex-app-server-capability-signal.ts", + "sha256": "43cf352901aeff4ed9143f51f224770f3dc92bf79dcfac6875bbc64105184c2f" + }, + { + "path": "src/main/codex/codex-process-exit-deadline.ts", + "sha256": "952cbb7c8778b8187ff443f8c045be53c14c3778af1b2ec77a3af5cd3ff13364" + }, + { + "path": "src/shared/system-cli-install-dirs.ts", + "sha256": "635b59f51022c9f31bf7720385b343018d4ce849220d422115e74f5351a9f21d" + }, + { + "path": "src/shared/node-cli-command-resolution.ts", + "sha256": "5eecee0c0d6296b962315c2ef2ae228a1bb1b6a7449f46d5afe3977f0af1e145" + }, + { + "path": "src/main/codex/codex-app-server-process-tree-kill.ts", + "sha256": "0a370b1d40509aa967634d6551f6baf5462b648910ddb79c2624555a84116b0b" + }, + { + "path": "src/shared/main-process-ndjson-framer.ts", + "sha256": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490" + }, + { + "path": "src/main/codex/codex-app-server-record-reader.ts", + "sha256": "b4d600293228ae2429090facc3db6f98dff449f2c3d82f35a0bea11fe4d70048" + }, + { + "path": "src/main/codex/codex-app-server-session.ts", + "sha256": "9adfbab8e229d3998d973ad9abfe647015ff21a5517fa6e5c7b32acf0a446eb8" + }, + { + "path": "src/main/codex/codex-app-server-exit-error.ts", + "sha256": "d58cdd97ae3ad4a786c7c4a0b1062ee3c5bb15b0943046dda7fff4b51ede21d1" + }, + { + "path": "src/main/codex/codex-app-server-handshake.ts", + "sha256": "3dc86d5961076713cb67ff58f593ca0fe848cd2f6b5a3a0a5da83773f49e6516" + }, + { + "path": "src/main/codex/codex-app-server-handshake-exit-proof.ts", + "sha256": "01832f6ab97bb68566221151d6a88e03cd5461f699991ad0e2c1f52573541ca1" + }, + { + "path": "src/shared/crash-reporting-diagnostic-bundle.ts", + "sha256": "2ac76a8941d4e8ae8d4e0a95ac9e10102602670e4c8fdbcb88a03b014903d1ba" + }, + { + "path": "src/shared/crash-report-signature-lines.ts", + "sha256": "564936f235f0c9faefc0c9f18f2fcbc588e5bef69ecd1071baac5a30a7c662a8" + }, + { + "path": "src/shared/posix-wait-status.ts", + "sha256": "1eb72525881baa2815548d31d2b83aaecf88e5dfd0258dbcaa5cda1e8fdcd69f" + }, + { + "path": "src/shared/crash-report-exit-code.ts", + "sha256": "a73f4e6fce72f11f5eec32c2a22d2e749f330f434aef20cec6dc03dbde55ebc6" + }, + { + "path": "src/shared/react-update-depth-attribution.ts", + "sha256": "15fd538fa4953088b63d9307fc575d4f795614b445a558340d80d61bb4c50929" + }, + { + "path": "src/shared/crash-report-attribution-lines.ts", + "sha256": "3db6cabca0444343347a90e6ec71a015a79489097e2484202cf2b57232483a6e" + }, + { + "path": "src/shared/crash-report-redaction.ts", + "sha256": "afd00bd49c77398425ee481070818e98067e90bb472a7454c571bf77ceee9c7f" + }, + { + "path": "src/shared/crash-reporting.ts", + "sha256": "e9749318038d145b7ae1819ffed6b285815c502d0eba13181c48b73b1bd3e918" + }, + { + "path": "src/main/observability/redactor.ts", + "sha256": "59190309dbef22508e3f2ef27b0190093d5aaab8cce146d6d49a89eb9d0a2b73" + }, + { + "path": "src/main/observability/tracer.ts", + "sha256": "82b6f4ec0be07b6aa612038b21b9f67b0ef199f694d29dc9f7b54b55dba9776e" + }, + { + "path": "src/main/crash-reporting/crash-breadcrumb-store.ts", + "sha256": "8848be8a897e0d1a214dbbaf87a0377d493ee83358763c0fc286e693889b2cb9" + }, + { + "path": "src/main/crash-reporting/main-process-lifecycle-identity.ts", + "sha256": "797cd37e5af6ec71f91636bc779274572a62ea67e5fa2874bb1f2f771020c950" + }, + { + "path": "src/main/crash-reporting/durable-crash-breadcrumb.ts", + "sha256": "477e28157e119f3bd85bc77d4adb185d36e44103b8a087d680715ce71b05dea7" + }, + { + "path": "src/main/crash-reporting/self-initiated-tree-kill-log.ts", + "sha256": "eb28b4a513a3cbc30586df30569adf29d828f059522ebe6c751d55b233c13987" + }, + { + "path": "src/shared/app-environment.ts", + "sha256": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140" + }, + { + "path": "src/main/orca-chromium-process-pids.ts", + "sha256": "960a15f005ff997fa379972a8c2abe3410b78c6facee449e8a5350754cfce965" + }, + { + "path": "src/main/own-chromium-tree-kill-guard.ts", + "sha256": "3a59cccd8cf923506aacad58f1c5d11b49f47a21e6c6dee8559912ee3c5cefb5" + }, + { + "path": "src/main/windows-process-tree-kill.ts", + "sha256": "83236378e93e3a15189ca02febf6b9b94d9d7eadafb46f0fcb72051904e6f45d" + }, + { + "path": "src/shared/process-table-index.ts", + "sha256": "30f236b7d0275f05673c0a7982bb0aa67c9319762749f5a583c5c31e51e31f36" + }, + { + "path": "src/shared/process-table-snapshot.ts", + "sha256": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9" + }, + { + "path": "src/shared/process-table-snapshot-reader.ts", + "sha256": "7d35601a2a7bf7a9c5b4a897995e0e8a15775b4d35939f3dd1abe1a042bb1c5e" + }, + { + "path": "src/main/windows/windows-command-line-recovery-health.ts", + "sha256": "a135da3611d54fecb0dba52b4d81e06ca1532680424b55e7efa315b7c886ec33" + }, + { + "path": "src/shared/child-process/windows-system-binary.ts", + "sha256": "0c42c7c454534a0937f4153e9708aeb243bd930d130ffc6493ac42d21405606e" + }, + { + "path": "src/main/windows/windows-process-table-cim-scan.ts", + "sha256": "ecfa487a3139cc087eae78ff61bf358f7eb62aec3b20e9d064d96b6e37862ab0" + }, + { + "path": "src/main/windows/windows-process-table.ts", + "sha256": "de0565f724f49ca393bb55c1618e697ea34e98bfee132c196d0c3e97713aabe1" + }, + { + "path": "src/main/providers/windows-foreground-process-rows.ts", + "sha256": "374accddf905f5b60fb1be4010e8160bfaf7f1a3834ecaffbf0119e555c7a765" + }, + { + "path": "src/main/windows-pty-root-identity.ts", + "sha256": "5e1ec017199f1a78150064d22eca4fd724874473c042f78fa236f3831f761711" + }, + { + "path": "src/main/pty-descendant-termination.ts", + "sha256": "dbdfe91d9248b4fbfbdac1733237ff3a4776e596a6a127fbdf5c4fb1d987b61e" + }, + { + "path": "src/main/pty-descendant-exit-verification.ts", + "sha256": "1f7a14a8273a228180538b3d37a20e0631e60a2e497134de499907eabfeb9b77" + }, + { + "path": "src/main/runtime/agent-session-process-identity-probe.ts", + "sha256": "4f6bc0fc286f74b07a26bdfbe69a9124498deb289c5d96db8b1334e12fdcce33" + }, + { + "path": "src/main/codex/codex-structured-owner-identity.ts", + "sha256": "b5b286f638515cae36539d0061c3808e64e6b2ed464f9326255fcd2f655d85f7" + }, + { + "path": "src/main/runtime/agent-session-spawn-token-readback.ts", + "sha256": "1b2481a5487b9ee7e21a2103b468e69d91879f84a2a330b7fa81c7a3b056e399" + }, + { + "path": "src/main/runtime/agent-session-spawn-token-process-scan.ts", + "sha256": "57fe5ac196d53e266b4d5e9ea0fab7311a292b5f281cbe56eea98c5579634bfc" + }, + { + "path": "src/main/codex/codex-app-server-process-teardown.ts", + "sha256": "183110df0c529c8eaec77ed457a9b17c5322d313482119f488f019dbf6d73f75" + }, + { + "path": "src/main/codex/codex-app-server-frame-size-error.ts", + "sha256": "8f567def6e41edb29c3a56b2be4377f64a99999c76bf24be2980a296ce6c01e5" + }, + { + "path": "src/main/codex/codex-app-server-jsonl.ts", + "sha256": "428b802a6704ec57efb26c696ad9e77da727a990b881665fdf5f68542cf143b4" + }, + { + "path": "src/main/codex/codex-app-server-request-error.ts", + "sha256": "67140be11e2b29f93d8a352552af733d228ac1808b6cf53bb7227e3906794eea" + }, + { + "path": "src/main/codex/codex-app-server-record-prefix.ts", + "sha256": "e70f8492995568e2891af0f58a78e5eb9a2f5f1970b9a561aa1bd7af32673f0d" + }, + { + "path": "src/main/codex/codex-app-server-record-dispatch.ts", + "sha256": "48554c6f9d4cb056d1a8e2e495b1c3fdadecf891d8f02d9599a7af3989f6ab23" + }, + { + "path": "src/main/codex/codex-app-server-connection.ts", + "sha256": "30fcca9f4cda5d6cceb9215905d7f3ead2361764b80e0e67ba0bcbb55e00672d" + }, + { + "path": "src/main/codex/codex-structured-thread-facts.ts", + "sha256": "a244c847c5ca22c3e3fac3b075b51f0a566f8cf6726731f69f50f7eb079b063a" + }, + { + "path": "src/main/codex/codex-structured-turn-processes.ts", + "sha256": "6a942ed29995d8001f8c041c693ed7bd30ecfc3fac5dd30e4c5ec70c0a12396a" + }, + { + "path": "src/main/codex/codex-structured-turn-cancellation.ts", + "sha256": "518b8e5a2f6dc4f8e46e01366c0a61830d6c805b68101484726bd62bbfc831a8" + }, + { + "path": "src/shared/orca-dispatch-status-prompt.ts", + "sha256": "fd9fedb3c839cd9d17b545329937c8fb6b9fa081120eb4678a9fb66f4252629e" + }, + { + "path": "src/shared/agent-status-field-normalization.ts", + "sha256": "a60c119eade7026c9e6f37ff98e77e718bd0cb41e929b0e256872981411e3328" + }, + { + "path": "src/main/native-chat/agent-session-wire/provider-frame-activity.ts", + "sha256": "9888661f598cee4d622f22b208f9ba1fc6615df6424328cf818cebe9fe86b8d7" + }, + { + "path": "src/main/codex/codex-subagent-activity.ts", + "sha256": "5cdf18a4ea2c68178a67a845d4c95672a0d94bb282e8e15c3a930c6dd59151e4" + }, + { + "path": "src/shared/native-chat-types.ts", + "sha256": "ca9440aba61822a8b92cc1009790d5f6e7bdb2f570cf159349a42027dfd40def" + }, + { + "path": "src/shared/native-chat-subagent-summary.ts", + "sha256": "a2cdf19a07edf3b50a971af22044608d50ea9cfebd07abc01af38755a2143077" + }, + { + "path": "src/main/codex/codex-subagent-executions.ts", + "sha256": "2a356e9a108a302e1131d40112401a48c582b2d5560eab073004a7efefde5259" + }, + { + "path": "src/main/codex/codex-subagent-group-body.ts", + "sha256": "9ff41e055ee33fb556f2079604271f13e696ca0114eaaa9a5f25166e5fa49280" + }, + { + "path": "src/main/codex/codex-structured-journal-limits.ts", + "sha256": "2fd2d5015b73fb948df6abdb6469039b1ee3a680db5119b1a44ebf82bc152492" + }, + { + "path": "src/main/codex/codex-subagent-roster.ts", + "sha256": "bafd291665fb178b0912bd2a2723910c00890f0766aae7f70f8bed5d417542b1" + }, + { + "path": "src/shared/native-chat-turn-status.ts", + "sha256": "4ce2b64fa7818106814c135e5daf77bb183f601c35f9f08e5b7ff847fba01378" + }, + { + "path": "src/shared/native-chat-tool-identity.ts", + "sha256": "8c98eec1a53b26a67ed48a0859c5863f5accbe67e013c4eb5a0f8b32441ee925" + }, + { + "path": "src/main/codex/codex-goal-journal-rows.ts", + "sha256": "415846ca105300349778da7637216ca6ce978c7036219f6b3038f7228ba8724e" + }, + { + "path": "src/main/native-chat/agent-session-wire/provider-frame-disposition.ts", + "sha256": "f24008cebb1b8a77b0cbd091e49140a4bf99c676bc61eedeb0a82f1263244a7c" + }, + { + "path": "src/main/native-chat/agent-session-wire/unhandled-provider-frame.ts", + "sha256": "f8169a024a573adfc97e4eb8c99ecc4e80f82e9f94a04a29011a6b8051cc7626" + }, + { + "path": "src/shared/raster-image-dimensions.ts", + "sha256": "0d7462b8b2e53bfcdcbd32803edcfb85ed0063f56d45cf230fb59aac4783d063" + }, + { + "path": "src/shared/raster-image-preview-limits.ts", + "sha256": "f2d836b354b3951912f1ec689d84b82aa1aade89ed8e9c6f76be2321d600e9a7" + }, + { + "path": "src/shared/raster-image-base64-preview.ts", + "sha256": "89baf40f4637615456888e0f35115517a8445a3b1cb640b7bc495dd9a98fcedb" + }, + { + "path": "src/shared/image-data-uri.ts", + "sha256": "3eb9d2bda499b8f10c74783f9de766041469b140a9b96b2b5be7d97528688ce0" + }, + { + "path": "src/main/codex/codex-image-item-translation.ts", + "sha256": "c4f88236c707472cbcfcdfb1a04e51c738f8741aa5847a33f730759499522f5e" + }, + { + "path": "src/main/codex/codex-command-action-class.ts", + "sha256": "93b7bceb66a9e5bfae680e018b61bddb5c1117fe26243088ceccc8738841f966" + }, + { + "path": "src/main/codex/codex-thread-item-identity.ts", + "sha256": "04b98b367a4d658fb759ebc4fc73ea4ae731ae3f238f191c8ce002b2a2c9e2a8" + }, + { + "path": "src/main/codex/codex-turn-ordinals.ts", + "sha256": "9b7cf66986235bcacd6c198950452c7f40bd559a3a0ce20995aac496a1e32d7f" + }, + { + "path": "src/main/codex/codex-structured-item-translation.ts", + "sha256": "8ab9debb279baf3a5043ffe754c8911755c6e4ca9edfc0a094671c4d5d166639" + }, + { + "path": "src/main/codex/codex-structured-journal-contracts.ts", + "sha256": "59dbe0f4f38aea3ff31ae62d7a8d244636e2813bb4a0f1e1c2db3362bdf193ac" + }, + { + "path": "src/main/codex/codex-structured-journal-generic-frames.ts", + "sha256": "5fe2414993c44075d9c979d76091c89611360cb166f650e4483d1e6bad3b30d2" + }, + { + "path": "src/main/native-chat/agent-session-wire/structured-session-compaction.ts", + "sha256": "c73d272d59d71522d3d61eea66cf61da205c739743d720d15cb70f78bd5e07ac" + }, + { + "path": "src/shared/agent-session-journal-item-key.ts", + "sha256": "09ebe4758e3f38b5b7591f07f7b4dbfd04c7cc01713c9b0afe03b07b656d1fe8" + }, + { + "path": "src/shared/agent-session-journal-types.ts", + "sha256": "4f85b301aa12ea14ac87b0099135247ad0758529cdb2ce003bb6a1031cf279ea" + }, + { + "path": "src/shared/agent-session-journal-schemas.ts", + "sha256": "579ab7671b7f8e46374f11b6ac669bf659bd5505345c49d7dd1c9acc4b0ef564" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-row-schema.ts", + "sha256": "de9c4f6324feef96fd33cfb0698f4c34d7e02694809fb133757146ead0cbb9c3" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-lifecycle-batch-partition.ts", + "sha256": "9361c2255ac501b4c18baf22dd4e74936bc8c1181f227329b97bf1af7593bf4c" + }, + { + "path": "src/main/codex/codex-structured-journal-sink.ts", + "sha256": "e05226f2cb906557a0811de780b9f30e4bbedbd6253cd4948e5518646b63cf26" + }, + { + "path": "src/main/codex/codex-structured-journal-compactions.ts", + "sha256": "45c8e450be19cf8b88b5ffa90311e80390f40a32fd2ceaddf6bfd4cf7992ba03" + }, + { + "path": "src/main/codex/codex-goal-journal-identity.ts", + "sha256": "55d5c3a3120ea038aac9ae64cd51c5bfdf0e13af1306dca01009993cf482ad08" + }, + { + "path": "src/main/codex/codex-structured-journal-goals.ts", + "sha256": "d14794482031e86cf21f2c859ce8097f0759a5a0b2bc8f97ea26598ecfd190a5" + }, + { + "path": "src/shared/agent-turn-lifecycle-text.ts", + "sha256": "bf3a91d9c5157be19c9c88aed96e2c4e076e744362b34ddf2b466c707067e32a" + }, + { + "path": "src/shared/agent-session-turn-record.ts", + "sha256": "1a3df88a627b4744ccbe8168860033dfef66bd4ff028c6431af6a6ac2f63194f" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-terminal-settlement.ts", + "sha256": "9344b1593bf91a49a6e1d2fb9604e2890939eb1b1f0cadbbdae7194db57a8080" + }, + { + "path": "src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts", + "sha256": "48828e4ee21858075cbb87ec1caa4a82991a55f80928482615d1df7ec5dc0fb2" + }, + { + "path": "src/main/codex/codex-command-lifecycle.ts", + "sha256": "7b480d3b111304e3caff71768fb3b11ab6c36e1fa8f5b0440623a77cac2382c2" + }, + { + "path": "src/main/codex/codex-structured-item-stream-bounds.ts", + "sha256": "88e9efa8a9c163905f1a5eb5eaf316fba2f194d76c5a94ed7f55636cc0d74ae0" + }, + { + "path": "src/main/codex/codex-item-stream-retention.ts", + "sha256": "66d1f1d51ed6711e20f81698b14be2d003e899dbc587ac1306b6a656cf951b96" + }, + { + "path": "src/main/codex/codex-structured-item-stream-events.ts", + "sha256": "da2a9da9025af354ce705a190badadf822f9ac3b403eef04aa0ef8aaf6f023de" + }, + { + "path": "src/main/codex/codex-structured-item-streams.ts", + "sha256": "b70c3306e8bec3587342afc3bc156f40903513fa7ff3865b217ba4cc912cd89f" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-values.ts", + "sha256": "fb66d47e32f4040f1b248a886b077506d23d0492264412e6cc3c96b579de425d" + }, + { + "path": "src/main/codex/codex-structured-dispatch-echo.ts", + "sha256": "4be5e3b55bd0a5507878798f221e758444dc82ede2f14e741f08b6b85979cd7a" + }, + { + "path": "src/main/codex/codex-structured-journal-items.ts", + "sha256": "7a9dc981cdcb1931d620feaa5523b486600c50c241f223ffdc71aa9edeb4a5bb" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-prompt-body-bounds.ts", + "sha256": "e4382b9d5b8a9cdba94b2ee71188119790987b11fd0f8f9718ff4be899e9a9fa" + }, + { + "path": "src/main/codex/codex-structured-prompt-items.ts", + "sha256": "813e8fdf2768663e31822699a5a7311df8e714ca2a269dcbb1f42e8d196ac456" + }, + { + "path": "src/main/codex/codex-structured-journal-prompts.ts", + "sha256": "806f61f913c28593b02b6ea6dcb68559b126bf939b91b1e5cf4f43fedabf45a2" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-turns.ts", + "sha256": "9042b2393d7d8898ed4f7436669dfbc82504e15b9ea90eb00e1518ee4db254e0" + }, + { + "path": "src/main/codex/codex-structured-journal-settlement.ts", + "sha256": "761616c292e15894b337fdd171acd7c2c1272fcabdbad3ea460f8831a923c797" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-frames.ts", + "sha256": "0679aea6b6fc274448d007c292218f90bfdd8ad3ed4ce4a7d59d0a15aae9e455" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-restore.ts", + "sha256": "a2e521b7cae677a5b3fd671065e5e4b1c812e2444f272df0f79feddae3640652" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-turn-boundaries.ts", + "sha256": "bb482b2c49fa3754b76e1e749185bb2804409e017f909c0453022ead07f99a03" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-turn-state.ts", + "sha256": "f6dd637f0e77f193923c3f3843b86d12786b75b9ce76e5a7e057a7187a832fe5" + }, + { + "path": "src/main/codex/codex-structured-journal-translation.ts", + "sha256": "56e17b9649471e804d29d1d54a3e14b4298ca8af2e25c4759dc37fce000b06a1" + }, + { + "path": "src/main/codex/codex-server-request-disposition.ts", + "sha256": "ddd362e00a38ff0166d35bc4cca51e4f79e377f188a9e07f5966da7c056854a0" + }, + { + "path": "src/shared/node-bounded-json-stringify.ts", + "sha256": "fcadabc537aa3125d1ee514026ecca5b8e2a76aabca7e29f08d3d35008001622" + }, + { + "path": "src/shared/remote-runtime-client-error.ts", + "sha256": "593ff4ee6466c4b18b69f65a20631967205fb8e1bd5e46239cf7f526b764156a" + }, + { + "path": "src/shared/remote-runtime-memory-limits.ts", + "sha256": "862a317d7f564a2cc54cfa60b6c093de5dc8a61714eac45daa178608c1f6acb1" + }, + { + "path": "src/main/native-chat/agent-session-wire/agent-session-history-page-bounds.ts", + "sha256": "57e49a0ea59cdccf4a4fc1532d43d2c127b077233602139b2d36d79fd547fc9d" + }, + { + "path": "src/main/codex/codex-structured-rewind.ts", + "sha256": "710115f53bc673f7d7259ec9e1d6b02e7827c348225c1e6a2006ed5eaf06e6bc" + }, + { + "path": "src/main/codex/codex-structured-provider-events.ts", + "sha256": "a411dc378e521ea8fc915e2a2a21476341a30f440e8233ae5649205d2a1e2e44" + } + ] + }, + "candidate": { + "bundleSha256": "6b7ec91709cad63ae089187e297914243757d59991842be0c0c2eb92514b3548", + "dependencies": [ + { + "path": "src/main/native-chat/agent-session-journal/journal-payload-bounds.ts", + "sha256": "a421da6c6eee9346746a69f8ba7ff966ce81969dfa0a93f58784cac27cf8cdad" + }, + { + "path": "src/shared/agent-session-wire-refusals.ts", + "sha256": "25123cce70418deb3d2f75cf5c3b7431aa6a8c3b834f022cd697230345c595ea" + }, + { + "path": "src/shared/agent-session-background-task-wire.ts", + "sha256": "6a756544bb07864d5a6e4573992f4b3bb0d0788acb686c16da177095be23167c" + }, + { + "path": "src/shared/agent-session-wire.ts", + "sha256": "466b641465f92957dafe873c68d4cb42f50aeb0768a111397050b50aa96ac64f" + }, + { + "path": "src/main/codex/codex-prompt-registry-bounds.ts", + "sha256": "f6bfd21bcc2ccf7005a93a4764d17235887f11380b0b29301410b0f291a5c618" + }, + { + "path": "src/main/codex/codex-item-field-readers.ts", + "sha256": "aa4b178c562a63995b7de63c6f5daaf7b6b610fb163fb0a357cc230314076323" + }, + { + "path": "src/main/codex/codex-prompt-registry.ts", + "sha256": "3d33f98215b9547d661c0a4f6aa8b9a8a6a88fa953d90652cb3fb9953425a539" + }, + { + "path": "src/main/native-chat/agent-session-wire/structured-agent-session-adapter.ts", + "sha256": "9a7bb24e419dbd41f4aaaa20b87dd82859579d663ebb800fc605df4b471749ef" + }, + { + "path": "src/main/codex/codex-structured-prompt-replies.ts", + "sha256": "7b927e9910031ee80ab885ce1d713133d3ee614c5b6d460a5c91ff3768d9cb2e" + }, + { + "path": "src/shared/child-process/cancel-process-acquisition.ts", + "sha256": "709a04316da5f3528e33e02bbb6f054012714efc73628a04fd08289eae0c42d5" + }, + { + "path": "src/main/codex/codex-structured-acquisition-window.ts", + "sha256": "6828970d450a53fcb30ecc0ecb20d8452395bf579cc2295ee53415c299d039a4" + }, + { + "path": "src/main/codex/codex-structured-session-state.ts", + "sha256": "271837e87efac55238342a26ccdf350d9eb663b1d38a2553886429d1376ab1f4" + }, + { + "path": "src/main/codex/codex-structured-prompt-ownership.ts", + "sha256": "8d86f3784b79a1470e4002527bdfa505efe2762cba865de7716442f7d9730013" + }, + { + "path": "src/shared/child-process/windows-command-line.ts", + "sha256": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca" + }, + { + "path": "src/shared/child-process/windows-cmd-shim-resolution.ts", + "sha256": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3" + }, + { + "path": "src/shared/child-process/spawn-resolution.ts", + "sha256": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc" + }, + { + "path": "src/shared/child-process/process-tree-kill-gate.ts", + "sha256": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab" + }, + { + "path": "src/shared/child-process/process-tree-termination.ts", + "sha256": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120" + }, + { + "path": "src/shared/child-process/bounded-output-sink.ts", + "sha256": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865" + }, + { + "path": "src/shared/child-process/child-termination-reporter.ts", + "sha256": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0" + }, + { + "path": "src/shared/child-process/process-spec.ts", + "sha256": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289" + }, + { + "path": "src/shared/child-process/run-process.ts", + "sha256": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad" + }, + { + "path": "src/shared/child-process/retryable-process-exit-proof.ts", + "sha256": "411a39e4508763d8c4ef331bd16e88c1fe497f03188493241afcce6eab0a6e65" + }, + { + "path": "src/main/codex/codex-app-server-posix-supervisor.ts", + "sha256": "ccd4ff7a43d3ba92b9becab45304d5f5522f5c1d5aa53d379fdd47845d0fe545" + }, + { + "path": "src/main/codex/codex-app-server-capability-signal.ts", + "sha256": "43cf352901aeff4ed9143f51f224770f3dc92bf79dcfac6875bbc64105184c2f" + }, + { + "path": "src/main/codex/codex-process-exit-deadline.ts", + "sha256": "952cbb7c8778b8187ff443f8c045be53c14c3778af1b2ec77a3af5cd3ff13364" + }, + { + "path": "src/shared/system-cli-install-dirs.ts", + "sha256": "635b59f51022c9f31bf7720385b343018d4ce849220d422115e74f5351a9f21d" + }, + { + "path": "src/shared/node-cli-command-resolution.ts", + "sha256": "5eecee0c0d6296b962315c2ef2ae228a1bb1b6a7449f46d5afe3977f0af1e145" + }, + { + "path": "src/main/codex/codex-app-server-process-tree-kill.ts", + "sha256": "0a370b1d40509aa967634d6551f6baf5462b648910ddb79c2624555a84116b0b" + }, + { + "path": "src/shared/main-process-ndjson-framer.ts", + "sha256": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490" + }, + { + "path": "src/main/codex/codex-app-server-record-reader.ts", + "sha256": "b4d600293228ae2429090facc3db6f98dff449f2c3d82f35a0bea11fe4d70048" + }, + { + "path": "src/main/codex/codex-app-server-session.ts", + "sha256": "9adfbab8e229d3998d973ad9abfe647015ff21a5517fa6e5c7b32acf0a446eb8" + }, + { + "path": "src/main/codex/codex-app-server-exit-error.ts", + "sha256": "d58cdd97ae3ad4a786c7c4a0b1062ee3c5bb15b0943046dda7fff4b51ede21d1" + }, + { + "path": "src/main/codex/codex-app-server-handshake.ts", + "sha256": "3dc86d5961076713cb67ff58f593ca0fe848cd2f6b5a3a0a5da83773f49e6516" + }, + { + "path": "src/main/codex/codex-app-server-handshake-exit-proof.ts", + "sha256": "01832f6ab97bb68566221151d6a88e03cd5461f699991ad0e2c1f52573541ca1" + }, + { + "path": "src/shared/crash-reporting-diagnostic-bundle.ts", + "sha256": "2ac76a8941d4e8ae8d4e0a95ac9e10102602670e4c8fdbcb88a03b014903d1ba" + }, + { + "path": "src/shared/crash-report-signature-lines.ts", + "sha256": "564936f235f0c9faefc0c9f18f2fcbc588e5bef69ecd1071baac5a30a7c662a8" + }, + { + "path": "src/shared/posix-wait-status.ts", + "sha256": "1eb72525881baa2815548d31d2b83aaecf88e5dfd0258dbcaa5cda1e8fdcd69f" + }, + { + "path": "src/shared/crash-report-exit-code.ts", + "sha256": "a73f4e6fce72f11f5eec32c2a22d2e749f330f434aef20cec6dc03dbde55ebc6" + }, + { + "path": "src/shared/react-update-depth-attribution.ts", + "sha256": "15fd538fa4953088b63d9307fc575d4f795614b445a558340d80d61bb4c50929" + }, + { + "path": "src/shared/crash-report-attribution-lines.ts", + "sha256": "3db6cabca0444343347a90e6ec71a015a79489097e2484202cf2b57232483a6e" + }, + { + "path": "src/shared/crash-report-redaction.ts", + "sha256": "afd00bd49c77398425ee481070818e98067e90bb472a7454c571bf77ceee9c7f" + }, + { + "path": "src/shared/crash-reporting.ts", + "sha256": "e9749318038d145b7ae1819ffed6b285815c502d0eba13181c48b73b1bd3e918" + }, + { + "path": "src/main/observability/redactor.ts", + "sha256": "59190309dbef22508e3f2ef27b0190093d5aaab8cce146d6d49a89eb9d0a2b73" + }, + { + "path": "src/main/observability/tracer.ts", + "sha256": "82b6f4ec0be07b6aa612038b21b9f67b0ef199f694d29dc9f7b54b55dba9776e" + }, + { + "path": "src/main/crash-reporting/crash-breadcrumb-store.ts", + "sha256": "8848be8a897e0d1a214dbbaf87a0377d493ee83358763c0fc286e693889b2cb9" + }, + { + "path": "src/main/crash-reporting/main-process-lifecycle-identity.ts", + "sha256": "797cd37e5af6ec71f91636bc779274572a62ea67e5fa2874bb1f2f771020c950" + }, + { + "path": "src/main/crash-reporting/durable-crash-breadcrumb.ts", + "sha256": "477e28157e119f3bd85bc77d4adb185d36e44103b8a087d680715ce71b05dea7" + }, + { + "path": "src/main/crash-reporting/self-initiated-tree-kill-log.ts", + "sha256": "eb28b4a513a3cbc30586df30569adf29d828f059522ebe6c751d55b233c13987" + }, + { + "path": "src/shared/app-environment.ts", + "sha256": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140" + }, + { + "path": "src/main/orca-chromium-process-pids.ts", + "sha256": "960a15f005ff997fa379972a8c2abe3410b78c6facee449e8a5350754cfce965" + }, + { + "path": "src/main/own-chromium-tree-kill-guard.ts", + "sha256": "3a59cccd8cf923506aacad58f1c5d11b49f47a21e6c6dee8559912ee3c5cefb5" + }, + { + "path": "src/main/windows-process-tree-kill.ts", + "sha256": "83236378e93e3a15189ca02febf6b9b94d9d7eadafb46f0fcb72051904e6f45d" + }, + { + "path": "src/shared/process-table-index.ts", + "sha256": "30f236b7d0275f05673c0a7982bb0aa67c9319762749f5a583c5c31e51e31f36" + }, + { + "path": "src/shared/process-table-snapshot.ts", + "sha256": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9" + }, + { + "path": "src/shared/process-table-snapshot-reader.ts", + "sha256": "7d35601a2a7bf7a9c5b4a897995e0e8a15775b4d35939f3dd1abe1a042bb1c5e" + }, + { + "path": "src/main/windows/windows-command-line-recovery-health.ts", + "sha256": "a135da3611d54fecb0dba52b4d81e06ca1532680424b55e7efa315b7c886ec33" + }, + { + "path": "src/shared/child-process/windows-system-binary.ts", + "sha256": "0c42c7c454534a0937f4153e9708aeb243bd930d130ffc6493ac42d21405606e" + }, + { + "path": "src/main/windows/windows-process-table-cim-scan.ts", + "sha256": "ecfa487a3139cc087eae78ff61bf358f7eb62aec3b20e9d064d96b6e37862ab0" + }, + { + "path": "src/main/windows/windows-process-table.ts", + "sha256": "de0565f724f49ca393bb55c1618e697ea34e98bfee132c196d0c3e97713aabe1" + }, + { + "path": "src/main/providers/windows-foreground-process-rows.ts", + "sha256": "374accddf905f5b60fb1be4010e8160bfaf7f1a3834ecaffbf0119e555c7a765" + }, + { + "path": "src/main/windows-pty-root-identity.ts", + "sha256": "5e1ec017199f1a78150064d22eca4fd724874473c042f78fa236f3831f761711" + }, + { + "path": "src/main/pty-descendant-termination.ts", + "sha256": "dbdfe91d9248b4fbfbdac1733237ff3a4776e596a6a127fbdf5c4fb1d987b61e" + }, + { + "path": "src/main/pty-descendant-exit-verification.ts", + "sha256": "1f7a14a8273a228180538b3d37a20e0631e60a2e497134de499907eabfeb9b77" + }, + { + "path": "src/main/runtime/agent-session-process-identity-probe.ts", + "sha256": "4f6bc0fc286f74b07a26bdfbe69a9124498deb289c5d96db8b1334e12fdcce33" + }, + { + "path": "src/main/codex/codex-structured-owner-identity.ts", + "sha256": "b5b286f638515cae36539d0061c3808e64e6b2ed464f9326255fcd2f655d85f7" + }, + { + "path": "src/main/runtime/agent-session-spawn-token-readback.ts", + "sha256": "1b2481a5487b9ee7e21a2103b468e69d91879f84a2a330b7fa81c7a3b056e399" + }, + { + "path": "src/main/runtime/agent-session-spawn-token-process-scan.ts", + "sha256": "57fe5ac196d53e266b4d5e9ea0fab7311a292b5f281cbe56eea98c5579634bfc" + }, + { + "path": "src/main/codex/codex-app-server-process-teardown.ts", + "sha256": "183110df0c529c8eaec77ed457a9b17c5322d313482119f488f019dbf6d73f75" + }, + { + "path": "src/main/codex/codex-app-server-frame-size-error.ts", + "sha256": "8f567def6e41edb29c3a56b2be4377f64a99999c76bf24be2980a296ce6c01e5" + }, + { + "path": "src/main/codex/codex-app-server-jsonl.ts", + "sha256": "428b802a6704ec57efb26c696ad9e77da727a990b881665fdf5f68542cf143b4" + }, + { + "path": "src/main/codex/codex-app-server-request-error.ts", + "sha256": "67140be11e2b29f93d8a352552af733d228ac1808b6cf53bb7227e3906794eea" + }, + { + "path": "src/main/codex/codex-app-server-record-prefix.ts", + "sha256": "e70f8492995568e2891af0f58a78e5eb9a2f5f1970b9a561aa1bd7af32673f0d" + }, + { + "path": "src/main/codex/codex-app-server-record-dispatch.ts", + "sha256": "48554c6f9d4cb056d1a8e2e495b1c3fdadecf891d8f02d9599a7af3989f6ab23" + }, + { + "path": "src/main/codex/codex-app-server-connection.ts", + "sha256": "30fcca9f4cda5d6cceb9215905d7f3ead2361764b80e0e67ba0bcbb55e00672d" + }, + { + "path": "src/main/codex/codex-structured-thread-facts.ts", + "sha256": "a244c847c5ca22c3e3fac3b075b51f0a566f8cf6726731f69f50f7eb079b063a" + }, + { + "path": "src/main/codex/codex-structured-turn-processes.ts", + "sha256": "6a942ed29995d8001f8c041c693ed7bd30ecfc3fac5dd30e4c5ec70c0a12396a" + }, + { + "path": "src/main/codex/codex-structured-turn-cancellation.ts", + "sha256": "518b8e5a2f6dc4f8e46e01366c0a61830d6c805b68101484726bd62bbfc831a8" + }, + { + "path": "src/shared/orca-dispatch-status-prompt.ts", + "sha256": "fd9fedb3c839cd9d17b545329937c8fb6b9fa081120eb4678a9fb66f4252629e" + }, + { + "path": "src/shared/agent-status-field-normalization.ts", + "sha256": "a60c119eade7026c9e6f37ff98e77e718bd0cb41e929b0e256872981411e3328" + }, + { + "path": "src/main/native-chat/agent-session-wire/provider-frame-activity.ts", + "sha256": "9888661f598cee4d622f22b208f9ba1fc6615df6424328cf818cebe9fe86b8d7" + }, + { + "path": "src/main/codex/codex-subagent-activity.ts", + "sha256": "5cdf18a4ea2c68178a67a845d4c95672a0d94bb282e8e15c3a930c6dd59151e4" + }, + { + "path": "src/shared/native-chat-types.ts", + "sha256": "ca9440aba61822a8b92cc1009790d5f6e7bdb2f570cf159349a42027dfd40def" + }, + { + "path": "src/shared/native-chat-subagent-summary.ts", + "sha256": "a2cdf19a07edf3b50a971af22044608d50ea9cfebd07abc01af38755a2143077" + }, + { + "path": "src/main/codex/codex-subagent-executions.ts", + "sha256": "2a356e9a108a302e1131d40112401a48c582b2d5560eab073004a7efefde5259" + }, + { + "path": "src/main/codex/codex-subagent-group-body.ts", + "sha256": "9ff41e055ee33fb556f2079604271f13e696ca0114eaaa9a5f25166e5fa49280" + }, + { + "path": "src/main/codex/codex-structured-journal-limits.ts", + "sha256": "2fd2d5015b73fb948df6abdb6469039b1ee3a680db5119b1a44ebf82bc152492" + }, + { + "path": "src/main/codex/codex-subagent-roster.ts", + "sha256": "bafd291665fb178b0912bd2a2723910c00890f0766aae7f70f8bed5d417542b1" + }, + { + "path": "src/shared/native-chat-turn-status.ts", + "sha256": "4ce2b64fa7818106814c135e5daf77bb183f601c35f9f08e5b7ff847fba01378" + }, + { + "path": "src/shared/native-chat-tool-identity.ts", + "sha256": "8c98eec1a53b26a67ed48a0859c5863f5accbe67e013c4eb5a0f8b32441ee925" + }, + { + "path": "src/main/codex/codex-goal-journal-rows.ts", + "sha256": "415846ca105300349778da7637216ca6ce978c7036219f6b3038f7228ba8724e" + }, + { + "path": "src/main/native-chat/agent-session-wire/provider-frame-disposition.ts", + "sha256": "f24008cebb1b8a77b0cbd091e49140a4bf99c676bc61eedeb0a82f1263244a7c" + }, + { + "path": "src/main/native-chat/agent-session-wire/unhandled-provider-frame.ts", + "sha256": "f8169a024a573adfc97e4eb8c99ecc4e80f82e9f94a04a29011a6b8051cc7626" + }, + { + "path": "src/shared/raster-image-dimensions.ts", + "sha256": "0d7462b8b2e53bfcdcbd32803edcfb85ed0063f56d45cf230fb59aac4783d063" + }, + { + "path": "src/shared/raster-image-preview-limits.ts", + "sha256": "f2d836b354b3951912f1ec689d84b82aa1aade89ed8e9c6f76be2321d600e9a7" + }, + { + "path": "src/shared/raster-image-base64-preview.ts", + "sha256": "89baf40f4637615456888e0f35115517a8445a3b1cb640b7bc495dd9a98fcedb" + }, + { + "path": "src/shared/image-data-uri.ts", + "sha256": "3eb9d2bda499b8f10c74783f9de766041469b140a9b96b2b5be7d97528688ce0" + }, + { + "path": "src/main/codex/codex-image-item-translation.ts", + "sha256": "c4f88236c707472cbcfcdfb1a04e51c738f8741aa5847a33f730759499522f5e" + }, + { + "path": "src/main/codex/codex-command-action-class.ts", + "sha256": "93b7bceb66a9e5bfae680e018b61bddb5c1117fe26243088ceccc8738841f966" + }, + { + "path": "src/main/codex/codex-thread-item-identity.ts", + "sha256": "04b98b367a4d658fb759ebc4fc73ea4ae731ae3f238f191c8ce002b2a2c9e2a8" + }, + { + "path": "src/main/codex/codex-turn-ordinals.ts", + "sha256": "9b7cf66986235bcacd6c198950452c7f40bd559a3a0ce20995aac496a1e32d7f" + }, + { + "path": "src/main/codex/codex-structured-item-translation.ts", + "sha256": "8ab9debb279baf3a5043ffe754c8911755c6e4ca9edfc0a094671c4d5d166639" + }, + { + "path": "src/main/codex/codex-structured-journal-contracts.ts", + "sha256": "59dbe0f4f38aea3ff31ae62d7a8d244636e2813bb4a0f1e1c2db3362bdf193ac" + }, + { + "path": "src/main/codex/codex-structured-journal-generic-frames.ts", + "sha256": "5fe2414993c44075d9c979d76091c89611360cb166f650e4483d1e6bad3b30d2" + }, + { + "path": "src/main/native-chat/agent-session-wire/structured-session-compaction.ts", + "sha256": "c73d272d59d71522d3d61eea66cf61da205c739743d720d15cb70f78bd5e07ac" + }, + { + "path": "src/shared/agent-session-journal-item-key.ts", + "sha256": "09ebe4758e3f38b5b7591f07f7b4dbfd04c7cc01713c9b0afe03b07b656d1fe8" + }, + { + "path": "src/shared/agent-session-journal-types.ts", + "sha256": "4f85b301aa12ea14ac87b0099135247ad0758529cdb2ce003bb6a1031cf279ea" + }, + { + "path": "src/shared/agent-session-journal-schemas.ts", + "sha256": "579ab7671b7f8e46374f11b6ac669bf659bd5505345c49d7dd1c9acc4b0ef564" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-row-schema.ts", + "sha256": "de9c4f6324feef96fd33cfb0698f4c34d7e02694809fb133757146ead0cbb9c3" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-lifecycle-batch-partition.ts", + "sha256": "9361c2255ac501b4c18baf22dd4e74936bc8c1181f227329b97bf1af7593bf4c" + }, + { + "path": "src/main/codex/codex-structured-journal-sink.ts", + "sha256": "e05226f2cb906557a0811de780b9f30e4bbedbd6253cd4948e5518646b63cf26" + }, + { + "path": "src/main/codex/codex-structured-journal-compactions.ts", + "sha256": "45c8e450be19cf8b88b5ffa90311e80390f40a32fd2ceaddf6bfd4cf7992ba03" + }, + { + "path": "src/main/codex/codex-goal-journal-identity.ts", + "sha256": "55d5c3a3120ea038aac9ae64cd51c5bfdf0e13af1306dca01009993cf482ad08" + }, + { + "path": "src/main/codex/codex-structured-journal-goals.ts", + "sha256": "d14794482031e86cf21f2c859ce8097f0759a5a0b2bc8f97ea26598ecfd190a5" + }, + { + "path": "src/shared/agent-turn-lifecycle-text.ts", + "sha256": "bf3a91d9c5157be19c9c88aed96e2c4e076e744362b34ddf2b466c707067e32a" + }, + { + "path": "src/shared/agent-session-turn-record.ts", + "sha256": "1a3df88a627b4744ccbe8168860033dfef66bd4ff028c6431af6a6ac2f63194f" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-terminal-settlement.ts", + "sha256": "9344b1593bf91a49a6e1d2fb9604e2890939eb1b1f0cadbbdae7194db57a8080" + }, + { + "path": "src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts", + "sha256": "48828e4ee21858075cbb87ec1caa4a82991a55f80928482615d1df7ec5dc0fb2" + }, + { + "path": "src/main/codex/codex-command-lifecycle.ts", + "sha256": "7b480d3b111304e3caff71768fb3b11ab6c36e1fa8f5b0440623a77cac2382c2" + }, + { + "path": "src/main/codex/codex-structured-item-stream-bounds.ts", + "sha256": "88e9efa8a9c163905f1a5eb5eaf316fba2f194d76c5a94ed7f55636cc0d74ae0" + }, + { + "path": "src/main/codex/codex-item-stream-retention.ts", + "sha256": "66d1f1d51ed6711e20f81698b14be2d003e899dbc587ac1306b6a656cf951b96" + }, + { + "path": "src/main/codex/codex-structured-item-stream-events.ts", + "sha256": "da2a9da9025af354ce705a190badadf822f9ac3b403eef04aa0ef8aaf6f023de" + }, + { + "path": "src/main/codex/codex-structured-item-streams.ts", + "sha256": "b70c3306e8bec3587342afc3bc156f40903513fa7ff3865b217ba4cc912cd89f" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-values.ts", + "sha256": "fb66d47e32f4040f1b248a886b077506d23d0492264412e6cc3c96b579de425d" + }, + { + "path": "src/main/codex/codex-structured-dispatch-echo.ts", + "sha256": "4be5e3b55bd0a5507878798f221e758444dc82ede2f14e741f08b6b85979cd7a" + }, + { + "path": "src/main/codex/codex-structured-journal-items.ts", + "sha256": "7a9dc981cdcb1931d620feaa5523b486600c50c241f223ffdc71aa9edeb4a5bb" + }, + { + "path": "src/main/native-chat/agent-session-journal/journal-prompt-body-bounds.ts", + "sha256": "e4382b9d5b8a9cdba94b2ee71188119790987b11fd0f8f9718ff4be899e9a9fa" + }, + { + "path": "src/main/codex/codex-structured-prompt-items.ts", + "sha256": "813e8fdf2768663e31822699a5a7311df8e714ca2a269dcbb1f42e8d196ac456" + }, + { + "path": "src/main/codex/codex-structured-journal-prompts.ts", + "sha256": "806f61f913c28593b02b6ea6dcb68559b126bf939b91b1e5cf4f43fedabf45a2" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-turns.ts", + "sha256": "9042b2393d7d8898ed4f7436669dfbc82504e15b9ea90eb00e1518ee4db254e0" + }, + { + "path": "src/main/codex/codex-structured-journal-settlement.ts", + "sha256": "761616c292e15894b337fdd171acd7c2c1272fcabdbad3ea460f8831a923c797" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-frames.ts", + "sha256": "0679aea6b6fc274448d007c292218f90bfdd8ad3ed4ce4a7d59d0a15aae9e455" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-restore.ts", + "sha256": "a2e521b7cae677a5b3fd671065e5e4b1c812e2444f272df0f79feddae3640652" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-turn-boundaries.ts", + "sha256": "bb482b2c49fa3754b76e1e749185bb2804409e017f909c0453022ead07f99a03" + }, + { + "path": "src/main/codex/codex-structured-journal-translation-turn-state.ts", + "sha256": "f6dd637f0e77f193923c3f3843b86d12786b75b9ce76e5a7e057a7187a832fe5" + }, + { + "path": "src/main/codex/codex-structured-journal-translation.ts", + "sha256": "56e17b9649471e804d29d1d54a3e14b4298ca8af2e25c4759dc37fce000b06a1" + }, + { + "path": "src/main/codex/codex-server-request-disposition.ts", + "sha256": "ddd362e00a38ff0166d35bc4cca51e4f79e377f188a9e07f5966da7c056854a0" + }, + { + "path": "src/shared/node-bounded-json-stringify.ts", + "sha256": "fcadabc537aa3125d1ee514026ecca5b8e2a76aabca7e29f08d3d35008001622" + }, + { + "path": "src/shared/remote-runtime-client-error.ts", + "sha256": "593ff4ee6466c4b18b69f65a20631967205fb8e1bd5e46239cf7f526b764156a" + }, + { + "path": "src/shared/remote-runtime-memory-limits.ts", + "sha256": "862a317d7f564a2cc54cfa60b6c093de5dc8a61714eac45daa178608c1f6acb1" + }, + { + "path": "src/main/native-chat/agent-session-wire/agent-session-history-page-bounds.ts", + "sha256": "57e49a0ea59cdccf4a4fc1532d43d2c127b077233602139b2d36d79fd547fc9d" + }, + { + "path": "src/main/codex/codex-structured-rewind.ts", + "sha256": "710115f53bc673f7d7259ec9e1d6b02e7827c348225c1e6a2006ed5eaf06e6bc" + }, + { + "path": "src/main/codex/codex-structured-provider-events.ts", + "sha256": "a411dc378e521ea8fc915e2a2a21476341a30f440e8233ae5649205d2a1e2e44" + } + ] + } + } +} diff --git a/docs/audits/codex-prompt-claim-retention/reproduce.cjs b/docs/audits/codex-prompt-claim-retention/reproduce.cjs new file mode 100644 index 00000000000..9ffb5034373 --- /dev/null +++ b/docs/audits/codex-prompt-claim-retention/reproduce.cjs @@ -0,0 +1,111 @@ +const assert = require('node:assert/strict') +const { createHash } = require('node:crypto') +const { readFileSync, writeFileSync } = require('node:fs') +const { resolve, relative } = require('node:path') +const esbuild = require('esbuild') +const Module = require('node:module') + +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function') +const { root, before, after, hashes } = require('./sources.cjs')() +const sourcePath = 'src/main/codex/codex-prompt-registry.ts' +const source = before.get(resolve(root, sourcePath)) +const candidate = after.get(resolve(root, sourcePath)) +const hash = (value) => createHash('sha256').update(value).digest('hex') +const entry = ` +export { CodexPromptRegistry } from './src/main/codex/codex-prompt-registry'; +export { cancelCodexStructuredTurn } from './src/main/codex/codex-structured-prompt-ownership'; +export { CodexStructuredTurnCancellation } from './src/main/codex/codex-structured-turn-cancellation'; +export { createCodexJournalTranslator } from './src/main/codex/codex-structured-journal-translation'; +export { deliverCodexServerRequest, translateCodexNotification } from './src/main/codex/codex-structured-provider-events'; +` + +async function build(mode) { + const result = await esbuild.build({ + stdin: { + contents: entry, + resolveDir: root, + loader: 'ts', + sourcefile: 'codex-claim-proof-entry.ts' + }, + absWorkingDir: root, + bundle: true, + platform: 'node', + format: 'cjs', + packages: 'external', + write: false, + metafile: true, + logLevel: 'silent', + plugins: [ + { + name: 'candidate-only-in-memory', + setup(build) { + build.onLoad({ filter: /\/codex-prompt-registry\.ts$/ }, (args) => { + assert.equal(args.path, resolve(root, sourcePath)) + return { contents: mode === 'candidate' ? candidate : source, loader: 'ts' } + }) + } + } + ] + }) + const bundlePath = resolve(root, `codex-claim-${mode}-proof.cjs`) + const loaded = new Module(bundlePath, module) + loaded.filename = bundlePath + loaded.paths = Module._nodeModulePaths(root) + loaded._compile(result.outputFiles[0].text, bundlePath) + const dependencies = Object.keys(result.metafile.inputs) + .filter((path) => path.startsWith('src/')) + .map((path) => ({ + path, + sha256: hash( + path === sourcePath + ? mode === 'original' + ? source + : candidate + : readFileSync(resolve(root, path)) + ) + })) + return { api: loaded.exports, bundleSha256: hash(result.outputFiles[0].contents), dependencies } +} + +const run = require('./scenario.cjs') + +async function main() { + const deadline = setTimeout(() => { + process.stderr.write('proof deadline\n') + process.exit(2) + }, 20_000) + const results = {} + const versions = {} + for (const mode of ['original', 'candidate']) { + const built = await build(mode) + results[mode] = await run(built.api, mode) + versions[mode] = { bundleSha256: built.bundleSha256, dependencies: built.dependencies } + } + clearTimeout(deadline) + const report = { + capturedAt: new Date().toISOString(), + runtime: process.versions, + scope: + 'Actual registry, server-request translation, cancellation ownership/cancellation class, journal translator, delayed turn completion. Injected accepted sink, interrupt transport, and compaction lookup. No provider, process enumeration/termination, or host data. Bundles load in memory; only the requested report is written.', + sourceHashes: hashes, + countsOnly: true, + noPayloadAmplification: true, + results, + versions + } + const output = process.argv[2] ?? resolve(__dirname, 'node-results.json') + writeFileSync(output, `${JSON.stringify(report, null, 2)}\n`) + process.stdout.write( + `${JSON.stringify( + { output: relative(root, output), sourceHashes: report.sourceHashes, results }, + null, + 2 + )}\n` + ) +} + +main().catch((error) => { + process.stderr.write(`${error.stack}\n`) + process.exit(1) +}) diff --git a/docs/audits/codex-prompt-claim-retention/scenario.cjs b/docs/audits/codex-prompt-claim-retention/scenario.cjs new file mode 100644 index 00000000000..8a2b9de0b0e --- /dev/null +++ b/docs/audits/codex-prompt-claim-retention/scenario.cjs @@ -0,0 +1,215 @@ +const assert = require('node:assert/strict') + +const admitted = () => ({ accepted: true }) + +function fixture(api) { + const prompts = new api.CodexPromptRegistry() + const state = { prompts, requestCount: 0, lastBinding: null, blockCompletion: false } + const sink = { + appendItem() {}, + appendTombstone() {}, + publish() {}, + tryAppendItem: admitted, + tryAppendTombstone: admitted, + tryAppendLifecycleBatch: (id) => + state.blockCompletion && id.startsWith('turn-completed:') + ? { accepted: false, reason: 'backpressure' } + : admitted(), + tryPublish: admitted + } + const translator = api.createCodexJournalTranslator({ + sink, + sessionId: 'session', + primaryThreadId: () => 'primary', + bindPromptItemId: (id, thread, promptKey, turn) => { + prompts.bindJournalItemId(id, thread, promptKey, turn) + state.lastBinding = id + }, + clearPromptTurn: (thread, turn) => prompts.clearTurn(thread, turn) + }) + const session = { + threadId: 'primary', + prompts, + translator, + fence: 7, + acquisitionGeneration: 'generation', + ended: false, + connection: { + request: async (method) => { + assert.equal(method, 'turn/interrupt') + state.requestCount++ + return {} + }, + respondWithError() { + throw new Error('unexpected server refusal') + }, + respond() { + throw new Error('unexpected prompt response') + } + } + } + const emit = (_session, event) => translator.handle(event) + const cancellation = new api.CodexStructuredTurnCancellation({ + emit, + captureTurnProcesses: async () => { + throw new Error('no process enumeration allowed') + }, + terminateTurnProcesses: async () => { + throw new Error('no process termination allowed') + } + }) + cancellation.register(session) + return Object.assign(state, { + api, + session, + translator, + cancellation, + emit, + sessions: new Map([['session', session]]), + compactions: { providerTurnId: () => 'primary-turn' } + }) +} + +function register( + state, + serial, + thread = `child-${serial}`, + turn = `turn-${serial}`, + item = `item-${serial}` +) { + state.lastBinding = null + const admission = state.api.deliverCodexServerRequest( + 'session', + state.session, + { + id: serial, + method: 'item/commandExecution/requestApproval', + params: { threadId: thread, turnId: turn, itemId: item, command: 'echo bounded-proof' } + }, + state.emit + ) + assert.equal(admission.accepted, true) + assert.equal(typeof state.lastBinding, 'string') + const prompt = state.prompts.find(state.lastBinding) + assert.ok(prompt) + return { ref: new WeakRef(prompt), id: state.lastBinding, thread, turn } +} + +async function cancel(state, record) { + const result = await state.api.cancelCodexStructuredTurn({ + sessions: state.sessions, + compactions: state.compactions, + cancellation: state.cancellation, + request: { + sessionId: 'session', + turnId: 'primary-turn', + fence: 7, + prompt: { itemId: record.id, kind: 'approval' } + } + }) + assert.equal(result.cancelled, true) +} + +function complete(state, thread, turn, expectedAccepted = true) { + const admission = state.api.translateCodexNotification({ + sessionId: 'session', + session: state.session, + method: 'turn/completed', + params: { threadId: thread, turn: { id: turn, status: 'interrupted' } }, + turnCancellation: state.cancellation, + emit: state.emit + }) + assert.equal(admission.accepted, expectedAccepted) +} + +async function alive(records) { + for (let round = 0; round < 8; round++) { + await new Promise(setImmediate) + global.gc() + } + return records.filter((record) => record.ref.deref() !== undefined).length +} + +async function run(api, mode) { + const state = fixture(api) + const ordinary = register(state, 1) + await cancel(state, ordinary) + assert.equal(await alive([ordinary]), 1) + complete(state, ordinary.thread, ordinary.turn) + const ordinaryAfterCompletion = await alive([ordinary]) + assert.equal(ordinaryAfterCompletion, 0) + + const records = [] + for (let index = 0; index < 32; index++) { + const record = register(state, index + 10) + await cancel(state, record) + records.push(record) + } + assert.equal(await alive(records), 32) + // Unrelated child traffic evicts old binding/address entries without ending their turns. + for (let index = 0; index < 256; index++) { + register(state, index + 1000, 'other-child', 'other-turn') + } + const sizesAfterEviction = state.prompts.sizes + for (const record of records) { + assert.equal(state.prompts.find(record.id), null) + } + const afterEvictionBeforeCompletion = await alive(records) + assert.equal(afterEvictionBeforeCompletion, 32) + complete(state, 'wrong-child', records[0].turn) + complete(state, records[0].thread, 'wrong-turn') + assert.equal(await alive(records), 32) + register(state, 5000, records[0].thread, records[0].turn) + state.blockCompletion = true + complete(state, records[0].thread, records[0].turn, false) + assert.equal(await alive(records), 32) + state.blockCompletion = false + for (const record of records) { + complete(state, record.thread, record.turn) + } + const afterExactTurnCompletion = await alive(records) + assert.equal(afterExactTurnCompletion, mode === 'original' ? 32 : 0) + complete(state, 'other-child', 'other-turn') + assert.deepEqual(state.prompts.sizes, { prompts: 0, journalBindings: 0 }) + const afterAllLookupMapsEmpty = await alive(records) + assert.equal(afterAllLookupMapsEmpty, mode === 'original' ? 32 : 0) + state.prompts.clear() + const afterSessionClear = await alive(records) + assert.equal(afterSessionClear, 0) + + // Replacing a journal address must not let old-turn completion clear the new prompt/claim. + const old = register(state, 2000, 'reuse-child', 'old-turn', 'reused-item') + await cancel(state, old) + const newer = register(state, 2001, 'reuse-child', 'new-turn', 'reused-item') + assert.equal(newer.id, old.id) + const replacementClaim = state.prompts.claimBound(newer.id) + assert.ok(replacementClaim) + complete(state, old.thread, old.turn) + assert.equal( + state.prompts.ownsBoundClaim(replacementClaim, newer.id, newer.thread, newer.turn), + true + ) + const oldAfterReplacementCompletion = await alive([old]) + assert.equal(oldAfterReplacementCompletion, mode === 'original' ? 1 : 0) + state.prompts.releaseClaim(replacementClaim) + complete(state, newer.thread, newer.turn) + state.prompts.clear() + state.translator.dispose() + return { + ordinaryAfterCompletion, + cancelledPrompts: 32, + sizesAfterEviction, + afterEvictionBeforeCompletion, + afterExactTurnCompletion, + afterAllLookupMapsEmpty, + afterSessionClear, + oldAfterReplacementCompletion, + replacementClaimPreserved: true, + wrongThreadPreserved: true, + wrongTurnPreserved: true, + rejectedCompletionPreserved: true, + successfulInterruptRequests: state.requestCount + } +} + +module.exports = run diff --git a/docs/audits/codex-prompt-claim-retention/source-versions.json b/docs/audits/codex-prompt-claim-retention/source-versions.json new file mode 100644 index 00000000000..12b24d3a1d7 --- /dev/null +++ b/docs/audits/codex-prompt-claim-retention/source-versions.json @@ -0,0 +1,54 @@ +{ + "baselineHashes": { + "src/main/codex/codex-prompt-registry.ts": "254c530216f47abfc3572bb1689161ad3c10775af2cdceda7e94f443e52c0691" + }, + "namedRefs": [ + { + "ref": "HEAD", + "revision": "9e2c137548bf99f91255ab4862c01145e42a0883", + "sha256": "254c530216f47abfc3572bb1689161ad3c10775af2cdceda7e94f443e52c0691", + "matchesBaseline": true + }, + { + "ref": "origin/main", + "revision": "77cd61df396f25ec91ee2d5ddcbd1f55aa94f818", + "sha256": "254c530216f47abfc3572bb1689161ad3c10775af2cdceda7e94f443e52c0691", + "matchesBaseline": true + } + ], + "historicalRuntimeReproduced": false, + "callbackProvenance": [ + { + "path": "src/main/codex/codex-structured-session-acquire.ts", + "sha256": "71cd2bae18944c2aaa3ea2a1d00958890e4c8219d5aae9a4de48dd692562ace0" + }, + { + "path": "src/main/codex/codex-structured-session-adapter.ts", + "sha256": "eab8820250ebdb1b3f6b3ab9287e16686bd6766f5af5dd29e081d7e47f083b20" + }, + { + "path": "src/main/codex/codex-structured-provider-events.ts", + "sha256": "a411dc378e521ea8fc915e2a2a21476341a30f440e8233ae5649205d2a1e2e44" + }, + { + "path": "src/main/codex/codex-structured-prompt-ownership.ts", + "sha256": "8d86f3784b79a1470e4002527bdfa505efe2762cba865de7716442f7d9730013" + }, + { + "path": "src/main/codex/codex-structured-turn-cancellation.ts", + "sha256": "518b8e5a2f6dc4f8e46e01366c0a61830d6c805b68101484726bd62bbfc831a8" + }, + { + "path": "src/main/codex/codex-structured-journal-translation.ts", + "sha256": "56e17b9649471e804d29d1d54a3e14b4298ca8af2e25c4759dc37fce000b06a1" + }, + { + "path": "src/main/codex/codex-structured-journal-settlement.ts", + "sha256": "761616c292e15894b337fdd171acd7c2c1272fcabdbad3ea460f8831a923c797" + }, + { + "path": "src/main/codex/codex-structured-session-close.ts", + "sha256": "f85aa2cdcfd2ddf34ae0be8397a3896128f04a989eaff750f8e72eee3398b361" + } + ] +} diff --git a/docs/audits/codex-prompt-claim-retention/sources.cjs b/docs/audits/codex-prompt-claim-retention/sources.cjs new file mode 100644 index 00000000000..39fd73fbabf --- /dev/null +++ b/docs/audits/codex-prompt-claim-retention/sources.cjs @@ -0,0 +1,29 @@ +const assert = require('node:assert/strict') +const { createHash } = require('node:crypto') +const { readFileSync } = require('node:fs') +const { resolve } = require('node:path') +const { applyPatch, parsePatch, reversePatch } = require('diff') + +module.exports = function loadSources() { + const root = resolve(__dirname, '../../..') + const expected = JSON.parse(readFileSync(resolve(__dirname, 'source-versions.json'), 'utf8')) + const parsed = parsePatch(readFileSync(resolve(__dirname, 'fix.patch'), 'utf8')) + const before = new Map() + const after = new Map() + const hashes = {} + assert.equal(parsed.length, 1) + for (const patch of parsed) { + const path = patch.newFileName.replace(/^b\//, '') + assert.ok(Object.hasOwn(expected.baselineHashes, path), `Unexpected patch path: ${path}`) + const absolute = resolve(root, path) + const current = readFileSync(absolute, 'utf8') + const baseline = applyPatch(current, reversePatch(patch)) + assert.notEqual(baseline, false, `Source changed; review fix.patch: ${path}`) + const hash = (source) => createHash('sha256').update(source).digest('hex') + assert.equal(hash(baseline), expected.baselineHashes[path], `Baseline drift: ${path}`) + before.set(absolute, baseline) + after.set(absolute, current) + hashes[path] = { before: hash(baseline), after: hash(current) } + } + return { root, before, after, hashes } +} diff --git a/docs/audits/codex-prompt-claim-retention/validation.json b/docs/audits/codex-prompt-claim-retention/validation.json new file mode 100644 index 00000000000..d32b06104e8 --- /dev/null +++ b/docs/audits/codex-prompt-claim-retention/validation.json @@ -0,0 +1,69 @@ +{ + "backgroundLaunch": true, + "newRegressionCases": 4, + "before": { + "config": "docs/audits/codex-prompt-claim-retention/before.config.mjs", + "passed": 31, + "failed": 3, + "exitCode": 1, + "paths": [ + "src/main/codex/codex-prompt-registry-retention.test.ts", + "src/main/codex/codex-structured-prompt-ownership.test.ts", + "src/main/codex/codex-structured-prompt-replies.test.ts" + ], + "expectedFailures": [ + "releases 32 evicted claims only when their exact turn completes", + "preserves a replacement prompt and its active claim when the old turn completes", + "finds an evicted claim through its bounded turn digest" + ] + }, + "after": { + "config": "config/vitest.config.ts", + "passed": 71, + "failed": 0, + "exitCode": 0, + "paths": [ + "src/main/codex/codex-prompt-registry-retention.test.ts", + "src/main/codex/codex-structured-prompt-ownership.test.ts", + "src/main/codex/codex-structured-prompt-replies.test.ts", + "src/main/codex/codex-structured-journal-translation-turn-lifecycle.test.ts", + "src/main/codex/codex-structured-journal-translation-settlement.test.ts", + "src/main/codex/codex-structured-session-close.test.ts" + ] + }, + "typechecks": { + "command": "node config/scripts/run-typecheck-projects-in-parallel.mjs", + "projects": [ + "config/tsconfig.node.json", + "config/tsconfig.tc.cli.json", + "config/tsconfig.tc.web.json" + ], + "exitCode": 0 + }, + "focusedOxlint": { + "ordinaryExitCode": 0, + "typeAwareExitCode": 0, + "noIgnore": true, + "files": 6 + }, + "changedCodeQuality": { + "command": "node config/scripts/check-changed-code-quality.mjs", + "base": "2fccacadbe23", + "changedFiles": 310, + "newFindings": 0, + "exitCode": 0 + }, + "proof": { + "command": "node --expose-gc --max-old-space-size=192 docs/audits/codex-prompt-claim-retention/reproduce.cjs", + "node": "26.6.0", + "electron": "43.7.0", + "electronNode": "24.21.0", + "bothExitCode": 0, + "beforeRetainedAfterExactCompletion": 32, + "afterRetainedAfterExactCompletion": 0, + "ordinaryPromptBytes": "not measured", + "payloadAmplification": false, + "ordering": "injected delayed child-turn completion, not an affected-host capture" + }, + "formatCheckExitCode": 0 +} diff --git a/src/main/codex/codex-prompt-registry-retention.test.ts b/src/main/codex/codex-prompt-registry-retention.test.ts new file mode 100644 index 00000000000..78e10cbcc77 --- /dev/null +++ b/src/main/codex/codex-prompt-registry-retention.test.ts @@ -0,0 +1,110 @@ +import { describe, expect, it } from 'vitest' +import { AGENT_SESSION_ID_MAX_LENGTH } from '../../shared/agent-session-wire' +import { CodexPromptRegistry, type CodexPendingPrompt } from './codex-prompt-registry' + +function registerPrompt( + registry: CodexPromptRegistry, + index: number, + threadId = 'thread', + turnId: string | null = 'turn', + itemId = `item-${index}` +): { itemId: string; prompt: CodexPendingPrompt } { + const prompt = registry.register({ + id: index, + method: 'item/commandExecution/requestApproval', + params: { itemId, threadId, turnId } + }) + if (!prompt) { + throw new Error('Fixture prompt was refused') + } + const journalItemId = `journal:${threadId}:${itemId}` + registry.bindJournalItemId(journalItemId, threadId, itemId, turnId) + return { itemId: journalItemId, prompt } +} + +function claimPrompt( + registry: CodexPromptRegistry, + index: number, + turnId = 'turn', + itemId?: string +): WeakRef { + const registered = registerPrompt(registry, index, 'thread', null, itemId) + registry.bindJournalItemId(registered.itemId, 'thread', registered.prompt.promptKey, turnId) + if (!registry.claimBound(registered.itemId)) { + throw new Error('Fixture prompt could not be claimed') + } + return new WeakRef(registered.prompt) +} + +async function collect(): Promise { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + for (let round = 0; round < 5; round += 1) { + await new Promise((resolve) => setImmediate(resolve)) + globalThis.gc() + } +} + +function evictLookupEntries(registry: CodexPromptRegistry): void { + for (let index = 0; index < 256; index += 1) { + registerPrompt(registry, index + 1_000, 'other-thread', 'other-turn') + } +} + +describe('Codex prompt claim lifetime', () => { + it('releases 32 evicted claims only when their exact turn completes', async () => { + const registry = new CodexPromptRegistry() + const prompts = Array.from({ length: 32 }, (_, index) => claimPrompt(registry, index)) + evictLookupEntries(registry) + expect(registry.sizes).toEqual({ prompts: 128, journalBindings: 256 }) + expect(registry.find('journal:thread:item-0')).toBeNull() + registry.clearTurn('other-thread', 'turn') + registry.clearTurn('thread', 'other-turn') + await collect() + expect(prompts.filter((prompt) => prompt.deref() !== undefined)).toHaveLength(32) + + registry.clearTurn('thread', 'turn') + await collect() + expect(prompts.filter((prompt) => prompt.deref() !== undefined)).toHaveLength(0) + expect(registry.sizes).toEqual({ prompts: 128, journalBindings: 256 }) + registry.clear() + }) + + it('preserves a replacement prompt and its active claim when the old turn completes', async () => { + const registry = new CodexPromptRegistry() + const old = claimPrompt(registry, 1, 'old-turn', 'same-item') + const replacement = registerPrompt(registry, 2, 'thread', 'new-turn', 'same-item') + const claim = registry.claimBound(replacement.itemId) + if (!claim) { + throw new Error('Replacement prompt could not be claimed') + } + registry.clearTurn('thread', 'old-turn') + await collect() + expect(old.deref()).toBeUndefined() + expect(registry.find(replacement.itemId)).toBe(replacement.prompt) + expect(registry.ownsBoundClaim(claim, replacement.itemId, 'thread', 'new-turn')).toBe(true) + registry.clearTurn('thread', 'new-turn') + expect(registry.ownsClaim(claim)).toBe(false) + }) + + it('finds an evicted claim through its bounded turn digest', async () => { + const registry = new CodexPromptRegistry() + const turnId = 'x'.repeat(AGENT_SESSION_ID_MAX_LENGTH + 1) + const prompt = claimPrompt(registry, 1, turnId) + evictLookupEntries(registry) + registry.clearTurn('thread', turnId) + await collect() + expect(prompt.deref()).toBeUndefined() + registry.clear() + }) + + it('releases evicted claims when the session is cleared', async () => { + const registry = new CodexPromptRegistry() + const prompt = claimPrompt(registry, 1) + evictLookupEntries(registry) + registry.clear() + await collect() + expect(prompt.deref()).toBeUndefined() + }) +}) diff --git a/src/main/codex/codex-prompt-registry.ts b/src/main/codex/codex-prompt-registry.ts index f3ba3fa3601..059f8a7a0d3 100644 --- a/src/main/codex/codex-prompt-registry.ts +++ b/src/main/codex/codex-prompt-registry.ts @@ -226,7 +226,7 @@ export class CodexPromptRegistry { clearTurn(threadId: string, turnId: string): void { const prompts = new Set( - [...this.byAddress.values(), ...this.boundPrompts.values()].filter( + [...this.byAddress.values(), ...this.boundPrompts.values(), ...this.claims.keys()].filter( (prompt) => prompt.threadId === threadId && codexPromptMatchesTurn(prompt, turnId) ) ) From 79800e60b4ba886da28bd65eaf9edbd264f570c7 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:12:13 -0700 Subject: [PATCH 054/168] fix: release completed terminal spawn inputs (#21139) Co-authored-by: m4air --- .../terminal-completed-spawn-inputs/README.md | 71 ++++++ .../admission-control.cjs | 172 ++++++++++++++ .../admission-electron.json | 84 +++++++ .../admission-node.json | 84 +++++++ .../baseline.config.mjs | 23 ++ .../electron-baseline.json | 66 ++++++ .../electron-fixed.json | 66 ++++++ .../terminal-completed-spawn-inputs/fix.patch | 122 ++++++++++ .../mapped-admission-electron.json | 84 +++++++ .../mapped-admission-node.json | 84 +++++++ .../mapped-electron-baseline.json | 66 ++++++ .../mapped-electron-fixed.json | 66 ++++++ .../mapped-node-baseline.json | 66 ++++++ .../mapped-node-fixed.json | 66 ++++++ .../node-baseline.json | 66 ++++++ .../node-fixed.json | 66 ++++++ .../reproduce.cjs | 208 +++++++++++++++++ .../source-versions.json | 113 +++++++++ .../spawn-fixture.cjs | 81 +++++++ .../spawn-source.cjs | 114 ++++++++++ src/main/daemon/session-output-pipeline.ts | 5 +- .../daemon/terminal-host-session-create.ts | 14 +- ...erminal-host-spawn-input-retention.test.ts | 214 ++++++++++++++++++ src/main/daemon/terminal-host.ts | 34 +-- 24 files changed, 2015 insertions(+), 20 deletions(-) create mode 100644 docs/audits/terminal-completed-spawn-inputs/README.md create mode 100644 docs/audits/terminal-completed-spawn-inputs/admission-control.cjs create mode 100644 docs/audits/terminal-completed-spawn-inputs/admission-electron.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/admission-node.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/baseline.config.mjs create mode 100644 docs/audits/terminal-completed-spawn-inputs/electron-baseline.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/electron-fixed.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/fix.patch create mode 100644 docs/audits/terminal-completed-spawn-inputs/mapped-admission-electron.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/mapped-admission-node.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/mapped-electron-baseline.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/mapped-electron-fixed.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/mapped-node-baseline.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/mapped-node-fixed.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/node-baseline.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/node-fixed.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/reproduce.cjs create mode 100644 docs/audits/terminal-completed-spawn-inputs/source-versions.json create mode 100644 docs/audits/terminal-completed-spawn-inputs/spawn-fixture.cjs create mode 100644 docs/audits/terminal-completed-spawn-inputs/spawn-source.cjs create mode 100644 src/main/daemon/terminal-host-spawn-input-retention.test.ts diff --git a/docs/audits/terminal-completed-spawn-inputs/README.md b/docs/audits/terminal-completed-spawn-inputs/README.md new file mode 100644 index 00000000000..bb8bae2dfbc --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/README.md @@ -0,0 +1,71 @@ +# Completed terminal spawns retain consumed inputs + +Status: reproduced against actual `TerminalHost`, `Session`, output pipeline, and daemon admission code on Node 26.6.0 and installed Electron 43.7.0 / Node 24.21.0. The fix releases completed request objects and consumed history seed arrays while the terminal remains alive. + +## Retaining paths and fix + +Three long-lived callbacks kept spawn-only input objects reachable: + +1. `terminal-host-session-create.ts::spawnAndPublishSession` gave `Session` an exit callback capturing the complete request and dependencies. A small factory now captures only the exit callback, session ID, and agent-session generation. +2. `TerminalHost.createOrAttach` constructed that exit callback beside the cancellation check that captures the request. Their shared lexical context kept the request reachable even after the first capture was projected. The unchanged exit/reap body now lives in a method bound to its host. +3. `session-output-pipeline.ts` captured pipeline options in its foreground-confirmation callback. Those options include history chunks that `SessionOutputPlane` has already consumed synchronously. The callback now captures the subprocess object; the liveness callback is also extracted before constructing the pipeline. + +The subprocess remains the receiver of `subprocess.confirmShellForeground?.()`. The only production provider of the exit callback is `TerminalHost`; its bound method preserves the host receiver. Exit codes, incarnation tombstones, claimed-generation release, reaping, cancellation, and process ownership follow the same paths. A constructor-only `maxTombstones` field was removed to keep `TerminalHost` within the existing line limit; the registry receives the same configured/default value directly. + +## Production reachability and limits + +- `daemon-provider-init.ts::initDaemonPtyProvider` installs the local daemon adapter. The cold-restore path in `daemon-pty-spawn-result.ts` supplies recovered history to terminal creation. `daemon-server.ts` owns the host and admission objects; `daemon-request-router.ts:59` routes `createOrAttach` to admission. +- `daemon-terminal-admission.ts:90` obtains inline history or takes completed transfer chunks, then passes the chunks, environment, and cancellation inputs into the host at line 96. `session-output-plane.ts:63` consumes all seed chunks into the emulator and retains the success flag. +- `terminal-history-seed-transfer-registry.ts:97` removes a completed transfer from its map and byte accounting when handing its chunks to creation. Its pending-transfer limits therefore do not bound the aggregate of already-consumed seeds retained by live sessions. The configured checkpoint maximum is 200,000,000 bytes, but these proofs use tiny seeds and do **not** measure a 200 MB allocation or incident-sized RSS. +- Retention lasts for the live session. Disposal permits collection even before the fix. This is avoidable retention per live terminal, not proof of unlimited growth after successful teardown. +- Real admission stream callbacks still keep preparation/signal metadata while attached: the routed-session getter shares the admission context with its cancellation callback (`daemon-terminal-admission.ts:117–122`). The admission control confirms those objects collect after public `host.detach` with the fix. This patch does not change that attached-stream lifetime. +- Native `pty-subprocess/subprocess-handle.ts:48–60` still captures its spawn arguments through the exit-status callback, including its merged environment object. Collection of the original request environment object does not prove all copied environment strings disappear from a real native process owner. The proof injects an inert subprocess and does not measure native allocations. +- The daemon path can run locally and on execution hosts used remotely. No wire fields or messages change, and folder workspaces require no special behavior. The finding is compatible with a local application memory report such as #19831, but no affected-host process/heap evidence establishes that the incident used this restore path or that it explains the reported magnitude. + +## Reproduction and controls + +`spawn-source.cjs` bundles actual source and reconstructs the baseline in memory by reversing `fix.patch`. SHA-256 checks fence both versions of all three changed modules using `source-versions.json`. The loader accepts the exact audit-branch pair and the exact independent-main publication pair; all other source hashes fail. Reports contain hashes of the source actually evaluated. Dependencies remain actual worktree code. Only the OS descendant-kill port is replaced with a throwing guard; subprocess handles are small injected objects, with no real shell, socket, process signal, or network activity. + +`reproduce.cjs` measures weak references to request, environment, history array, and cancellation signal objects. It also tests pending ownership, actual exit/reap and claimed-generation replacement, retired-incarnation exit evidence, and foreground confirmation with the correct subprocess receiver and queued prompt delivery. + +| Check | Baseline | Fixed | +| --------------------------------------------------------- | ------------------------------- | -------------------- | +| Three completed requests while three sessions remain live | 3 of each input object retained | 0 of each retained | +| One request during unresolved spawn | All four input objects retained | All four retained | +| That request after publication | All four retained | All four collectible | +| Inputs after disposal | All collectible | All collectible | +| Exit/reap, new incarnation/generation, shell confirmation | Pass | Pass | + +`admission-control.cjs` exercises actual daemon admission and preparations above the actual host. A forwarding observer stores only weak references. Transport, attachment bookkeeping, and native subprocess ports are inert. Both runtimes reproduce the following: + +| Admission phase | Original options/env/history | Preparation/signal | Request/payload | +| ------------------------------------------- | ---------------------------- | ------------------ | --------------- | +| Baseline, attached or detached live session | Retained | Retained | Collectible | +| Fixed, attached live session | Collectible | Retained | Collectible | +| Fixed, detached live session | Collectible | Collectible | Collectible | +| Either version after disposal | Collectible | Collectible | Collectible | + +The seeded snapshot remains readable after collection. These object reachability checks establish specific removed retaining paths; they do not establish total memory released. No heap-snapshot tool was exposed in this session. The historical Electron 43.4.1 binary was not tested. Each process uses a 192 MiB old-space limit and a 15-second deadline. + +Run from the worktree: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/terminal-completed-spawn-inputs/reproduce.cjs --baseline +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/terminal-completed-spawn-inputs/reproduce.cjs +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/terminal-completed-spawn-inputs/admission-control.cjs +ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/daemon/terminal-host-spawn-input-retention.test.ts +``` + +For Electron, run the same proof scripts with the binary returned by `require('electron')`, the same Node flags, `ELECTRON_RUN_AS_NODE=1`, and `ORCA_BACKGROUND_LAUNCH=1`. This starts no application or window. Node and Electron reports are stored separately in this directory. + +The four permanent regressions pass with the fix. The reconstructed baseline deliberately fails the two retention regressions and passes both lifecycle controls: `ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config docs/audits/terminal-completed-spawn-inputs/baseline.config.mjs` exits 1. Existing host, concurrent create, teardown/recreate, reaping, agent ownership, preflight replacement, and history restore tests also pass: 67 tests across nine files. Node typecheck and the changed-code quality gate passed; explicit basic/type-aware lint includes the audit scripts. + +## Source identity and compatibility + +`source-versions.json` records the exact audited branch baseline, fixed hashes, previously reviewed main commit `77cd61df396f25ec91ee2d5ddcbd1f55aa94f818`, release `v1.4.198` commit `e0826956fcfc532f5a1e55b5e081f2e57e553c43`, and independent publication main commit `291b4ddd6f1c1af480169885e0fda7f9c78ff053`. The create and pipeline files exactly match these historical baselines. Historical `TerminalHost` differs only in the unrelated producer pause/resume source parameter from #20947 on the audit branch. This fix applies independently and does not require #20947. + +The supported `TerminalHost` SHA-256 pairs are audit baseline `8cd6804b249ffb0d82da7f5a7ec8faa66514b0d6e3e36472cd81e6409c3edbc4` → fixed `23cfd9c6317db30edd48a0c8bd7c54658206654703461cf12295274b23fb8844`, and independent-main baseline `5216562b3c10c5fce4238614dc3a81887c970359a60e7e23179c6dfa15ccbcca` → fixed `f22856504559a6bb78498c6d5aae07cbbd80a21019723278a560628e0142236f`. Each selected fixed source is reverse-patched and checked against its own paired baseline hash. + +The four permanent tests pass when the three patched main modules are overlaid on current dependencies. The six `mapped-*.json` reports repeat both runtime proofs and admission controls using the exact patched publication-main modules. They report the main hashes actually evaluated. This is a narrow compatibility check with working-tree dependencies, not a full historical application build. An optional `ORCA_SPAWN_INPUT_PROOF_SOURCE_MAP` points to a JSON object from these three relative source paths to exact reviewed fixed-source strings; unknown or incomplete mappings fail the same hash checks. With no mapping, the loader checks the published checkout directly. Mapped runs write separate reports prefixed `mapped-`. + +Cancellation wait/listener findings from the preceding audit remain diagnostic and are outside this patch. The independent admission review narrowed the signal/environment claims before publication. diff --git a/docs/audits/terminal-completed-spawn-inputs/admission-control.cjs b/docs/audits/terminal-completed-spawn-inputs/admission-control.cjs new file mode 100644 index 00000000000..de421ae36ad --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/admission-control.cjs @@ -0,0 +1,172 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const { + loadExports, + evaluatedSourceHashes, + sourceMode, + reportPrefix, + sha +} = require('./spawn-source.cjs') +const { subprocess, collect } = require('./spawn-fixture.cjs') + +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function') +const root = path.resolve(__dirname, '../../..') + +function observeOptions(refs, host) { + return { + createOrAttach(options) { + refs.options = new WeakRef(options) + refs.env = new WeakRef(options.env) + refs.history = new WeakRef(options.historySeedChunks) + refs.signal = new WeakRef(options.cancelSignal) + return host.createOrAttach(options) + }, + detach: (...args) => host.detach(...args) + } +} + +function observePreparations(refs, preparations) { + return { + register(...args) { + const preparation = preparations.register(...args) + refs.preparation = new WeakRef(preparation) + return preparation + }, + prepareUnlessCanceled: (...args) => preparations.prepareUnlessCanceled(...args), + finish: (...args) => preparations.finish(...args) + } +} + +async function create(admission, refs) { + const request = { + id: 'request', + type: 'createOrAttach', + payload: { + sessionId: 'admission-review', + cols: 80, + rows: 24, + env: { REVIEW: 'request-input' }, + historySeed: 'ADMISSION-HISTORY-SEED\r\n' + } + } + refs.request = new WeakRef(request) + refs.payload = new WeakRef(request.payload) + const result = await admission.createOrAttach('client', request) + assert.equal(result.isNew, true) + assert.equal(result.historySeeded, true) +} + +async function exercise(api) { + const refs = {} + const host = new api.TerminalHost({ spawnSubprocess: async () => subprocess() }) + const preparations = new api.DaemonPtySpawnPreparations(async () => {}) + const client = { authenticatedPairEstablished: true, streamSocket: {} } + const attachments = [] + const admission = new api.DaemonTerminalAdmission({ + host: observeOptions(refs, host), + preparations: observePreparations(refs, preparations), + connections: new Map([['client', client]]), + endpoint: { hasLostOwnership: () => false }, + attachments: { + attach(...args) { + attachments.push(args) + }, + release() {}, + lastInputAt: () => undefined + }, + historySeedTransfers: { + take() { + throw new Error('Inline history only') + } + }, + transientFactRelay: { isBackgrounded: () => false, onSessionData() {}, onSessionExit() {} }, + streamDataBatcher: { + enqueue() {}, + enqueueControlEvent() {}, + flush() {}, + refreshSessionDroppability() {} + }, + log: { log() {} }, + isAcceptingWork: () => true, + requestEndpointRetirement() { + throw new Error('Unexpected endpoint retirement') + }, + reevaluateIdleShutdown() {} + }) + const retained = () => + Object.fromEntries(Object.entries(refs).map(([key, ref]) => [key, ref.deref() !== undefined])) + try { + await create(admission, refs) + assert.equal(admission.inFlight, 0) + assert.equal(preparations.pending.size, 0) + await collect() + const attached = retained() + assert.equal(host.listSessions().length, 1) + assert.match(host.getSnapshot('admission-review').snapshotAnsi, /ADMISSION-HISTORY-SEED/) + assert.equal(attachments.length, 1) + host.detach('admission-review', attachments[0][2]) + await collect() + const detached = retained() + assert.equal(host.listSessions().length, 1) + await host.dispose() + await collect() + const disposed = retained() + assert(Object.values(disposed).every((value) => !value)) + return { attached, detached, disposed, historyVisibleAfterCollection: true } + } finally { + await host.dispose() + } +} + +async function main() { + const phases = {} + for (const phase of ['baseline', 'fixed']) { + const result = await exercise(await loadExports(phase === 'fixed')) + for (const key of ['options', 'env', 'history']) { + assert.equal(result.attached[key], phase === 'baseline') + assert.equal(result.detached[key], phase === 'baseline') + } + for (const key of ['preparation', 'signal']) { + assert.equal(result.attached[key], true) + assert.equal(result.detached[key], phase === 'baseline') + } + for (const key of ['request', 'payload']) { + assert.equal(result.attached[key], false) + assert.equal(result.detached[key], false) + } + phases[phase] = result + } + const sourceHashes = { ...evaluatedSourceHashes } + for (const file of [ + 'src/main/daemon/daemon-terminal-admission.ts', + 'src/main/daemon/daemon-pty-spawn-preparations.ts' + ]) { + sourceHashes[file] = sha(fs.readFileSync(path.join(root, file))) + } + const report = { + node: process.version, + electron: process.versions.electron ?? null, + v8: process.versions.v8, + sourceMode, + sourceHashes, + phases + } + fs.writeFileSync( + path.join( + __dirname, + `${reportPrefix}admission-${process.versions.electron ? 'electron' : 'node'}.json` + ), + `${JSON.stringify(report, null, 2)}\n` + ) + console.log(JSON.stringify(phases, null, 2)) +} +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) +setTimeout(() => { + console.error('fixture timeout') + process.exit(2) +}, 15000).unref() diff --git a/docs/audits/terminal-completed-spawn-inputs/admission-electron.json b/docs/audits/terminal-completed-spawn-inputs/admission-electron.json new file mode 100644 index 00000000000..98f93204cef --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/admission-electron.json @@ -0,0 +1,84 @@ +{ + "node": "v24.21.0", + "electron": "43.7.0", + "v8": "15.0.245.31-electron.0", + "sourceMode": "working-tree modules and dependencies", + "sourceHashes": { + "src/main/daemon/session-output-pipeline.ts": { + "baseline": "99ff9d936469a59e07f979b19344815c8636cd58a985e139614f221e99eaf8c9", + "fixed": "94f6f221d85388274d585ad714fda25c32a824942950074e4e95b85c94a6771f" + }, + "src/main/daemon/terminal-host-session-create.ts": { + "baseline": "44fb82244a3307b5c89ebf724640be585dfe1b79566d11649b38eaf518c54d32", + "fixed": "d54cbaff13c8daba1766e4fec24a6903ecdf1486249f1a9e5d96a207de410309" + }, + "src/main/daemon/terminal-host.ts": { + "baseline": "8cd6804b249ffb0d82da7f5a7ec8faa66514b0d6e3e36472cd81e6409c3edbc4", + "fixed": "23cfd9c6317db30edd48a0c8bd7c54658206654703461cf12295274b23fb8844" + }, + "src/main/daemon/daemon-terminal-admission.ts": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "src/main/daemon/daemon-pty-spawn-preparations.ts": "77c94466578a5066af069e45eb5e87a64b73b88580a13b2172b2ea846b434f4e" + }, + "phases": { + "baseline": { + "attached": { + "request": false, + "payload": false, + "preparation": true, + "options": true, + "env": true, + "history": true, + "signal": true + }, + "detached": { + "request": false, + "payload": false, + "preparation": true, + "options": true, + "env": true, + "history": true, + "signal": true + }, + "disposed": { + "request": false, + "payload": false, + "preparation": false, + "options": false, + "env": false, + "history": false, + "signal": false + }, + "historyVisibleAfterCollection": true + }, + "fixed": { + "attached": { + "request": false, + "payload": false, + "preparation": true, + "options": false, + "env": false, + "history": false, + "signal": true + }, + "detached": { + "request": false, + "payload": false, + "preparation": false, + "options": false, + "env": false, + "history": false, + "signal": false + }, + "disposed": { + "request": false, + "payload": false, + "preparation": false, + "options": false, + "env": false, + "history": false, + "signal": false + }, + "historyVisibleAfterCollection": true + } + } +} diff --git a/docs/audits/terminal-completed-spawn-inputs/admission-node.json b/docs/audits/terminal-completed-spawn-inputs/admission-node.json new file mode 100644 index 00000000000..84f7e83a4a1 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/admission-node.json @@ -0,0 +1,84 @@ +{ + "node": "v26.6.0", + "electron": null, + "v8": "14.6.202.34-node.26", + "sourceMode": "working-tree modules and dependencies", + "sourceHashes": { + "src/main/daemon/session-output-pipeline.ts": { + "baseline": "99ff9d936469a59e07f979b19344815c8636cd58a985e139614f221e99eaf8c9", + "fixed": "94f6f221d85388274d585ad714fda25c32a824942950074e4e95b85c94a6771f" + }, + "src/main/daemon/terminal-host-session-create.ts": { + "baseline": "44fb82244a3307b5c89ebf724640be585dfe1b79566d11649b38eaf518c54d32", + "fixed": "d54cbaff13c8daba1766e4fec24a6903ecdf1486249f1a9e5d96a207de410309" + }, + "src/main/daemon/terminal-host.ts": { + "baseline": "8cd6804b249ffb0d82da7f5a7ec8faa66514b0d6e3e36472cd81e6409c3edbc4", + "fixed": "23cfd9c6317db30edd48a0c8bd7c54658206654703461cf12295274b23fb8844" + }, + "src/main/daemon/daemon-terminal-admission.ts": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "src/main/daemon/daemon-pty-spawn-preparations.ts": "77c94466578a5066af069e45eb5e87a64b73b88580a13b2172b2ea846b434f4e" + }, + "phases": { + "baseline": { + "attached": { + "request": false, + "payload": false, + "preparation": true, + "options": true, + "env": true, + "history": true, + "signal": true + }, + "detached": { + "request": false, + "payload": false, + "preparation": true, + "options": true, + "env": true, + "history": true, + "signal": true + }, + "disposed": { + "request": false, + "payload": false, + "preparation": false, + "options": false, + "env": false, + "history": false, + "signal": false + }, + "historyVisibleAfterCollection": true + }, + "fixed": { + "attached": { + "request": false, + "payload": false, + "preparation": true, + "options": false, + "env": false, + "history": false, + "signal": true + }, + "detached": { + "request": false, + "payload": false, + "preparation": false, + "options": false, + "env": false, + "history": false, + "signal": false + }, + "disposed": { + "request": false, + "payload": false, + "preparation": false, + "options": false, + "env": false, + "history": false, + "signal": false + }, + "historyVisibleAfterCollection": true + } + } +} diff --git a/docs/audits/terminal-completed-spawn-inputs/baseline.config.mjs b/docs/audits/terminal-completed-spawn-inputs/baseline.config.mjs new file mode 100644 index 00000000000..82362685b1c --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/baseline.config.mjs @@ -0,0 +1,23 @@ +import { createRequire } from 'node:module' +import path from 'node:path' +import { defineConfig, mergeConfig } from 'vitest/config' +import rootConfig from '../../../config/vitest.config.ts' + +const require = createRequire(import.meta.url) +const { baselineSources } = require('./spawn-source.cjs') +const config = mergeConfig( + rootConfig, + defineConfig({ + plugins: [ + { + name: 'completed-spawn-input-baseline', + enforce: 'pre', + load(id) { + return baselineSources.get(path.normalize(id)) + } + } + ] + }) +) +config.test.include = ['src/main/daemon/terminal-host-spawn-input-retention.test.ts'] +export default config diff --git a/docs/audits/terminal-completed-spawn-inputs/electron-baseline.json b/docs/audits/terminal-completed-spawn-inputs/electron-baseline.json new file mode 100644 index 00000000000..448cb931379 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/electron-baseline.json @@ -0,0 +1,66 @@ +{ + "node": "v24.21.0", + "electron": "43.7.0", + "v8": "15.0.245.31-electron.0", + "fixed": false, + "sourceMode": "working-tree modules and dependencies", + "sourceHashes": { + "src/main/daemon/session-output-pipeline.ts": "99ff9d936469a59e07f979b19344815c8636cd58a985e139614f221e99eaf8c9", + "src/main/daemon/terminal-host-session-create.ts": "44fb82244a3307b5c89ebf724640be585dfe1b79566d11649b38eaf518c54d32", + "src/main/daemon/terminal-host.ts": "8cd6804b249ffb0d82da7f5a7ec8faa66514b0d6e3e36472cd81e6409c3edbc4" + }, + "reports": [ + { + "case": "completed-inputs", + "whileLive": { + "options": 3, + "env": 3, + "history": 3, + "signal": 3 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "liveSessionCountAtCollection": 3 + }, + { + "case": "pending-inputs", + "duringSpawn": { + "options": 1, + "env": 1, + "history": 1, + "signal": 1 + }, + "afterPublication": { + "options": 1, + "env": 1, + "history": 1, + "signal": 1 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + } + }, + { + "case": "exit-and-recreate", + "exitVerdict": "exited", + "exitReason": "pty_exit_7", + "reaped": ["claimed", "claimed"], + "newIncarnation": true, + "newGeneration": true + }, + { + "case": "foreground-confirmation", + "confirmations": 1, + "preservedReceiver": true, + "owner": "shell", + "queuedPromptReleased": true + } + ] +} diff --git a/docs/audits/terminal-completed-spawn-inputs/electron-fixed.json b/docs/audits/terminal-completed-spawn-inputs/electron-fixed.json new file mode 100644 index 00000000000..3351fc4d975 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/electron-fixed.json @@ -0,0 +1,66 @@ +{ + "node": "v24.21.0", + "electron": "43.7.0", + "v8": "15.0.245.31-electron.0", + "fixed": true, + "sourceMode": "working-tree modules and dependencies", + "sourceHashes": { + "src/main/daemon/session-output-pipeline.ts": "94f6f221d85388274d585ad714fda25c32a824942950074e4e95b85c94a6771f", + "src/main/daemon/terminal-host-session-create.ts": "d54cbaff13c8daba1766e4fec24a6903ecdf1486249f1a9e5d96a207de410309", + "src/main/daemon/terminal-host.ts": "23cfd9c6317db30edd48a0c8bd7c54658206654703461cf12295274b23fb8844" + }, + "reports": [ + { + "case": "completed-inputs", + "whileLive": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "liveSessionCountAtCollection": 3 + }, + { + "case": "pending-inputs", + "duringSpawn": { + "options": 1, + "env": 1, + "history": 1, + "signal": 1 + }, + "afterPublication": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + } + }, + { + "case": "exit-and-recreate", + "exitVerdict": "exited", + "exitReason": "pty_exit_7", + "reaped": ["claimed", "claimed"], + "newIncarnation": true, + "newGeneration": true + }, + { + "case": "foreground-confirmation", + "confirmations": 1, + "preservedReceiver": true, + "owner": "shell", + "queuedPromptReleased": true + } + ] +} diff --git a/docs/audits/terminal-completed-spawn-inputs/fix.patch b/docs/audits/terminal-completed-spawn-inputs/fix.patch new file mode 100644 index 00000000000..d18ce888276 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/fix.patch @@ -0,0 +1,122 @@ +diff --git a/src/main/daemon/session-output-pipeline.ts b/src/main/daemon/session-output-pipeline.ts +index c249e4d1d3..f67d2e15b9 100644 +--- a/src/main/daemon/session-output-pipeline.ts ++++ b/src/main/daemon/session-output-pipeline.ts +@@ -14,6 +14,7 @@ export function createSessionOutputPipeline(opts: { + subprocess: SubprocessHandle + isAlive: () => boolean + }): { output: SessionOutputPlane; recoveryBarrier: TerminalShellRecoveryBarrier } { ++ const { subprocess, isAlive } = opts + let barrier: TerminalShellRecoveryBarrier | null = null + const output = new SessionOutputPlane({ + cols: opts.cols, +@@ -24,9 +25,9 @@ export function createSessionOutputPipeline(opts: { + getTerminalOwner: () => barrier?.getOwner() + }) + const recoveryBarrier = new TerminalShellRecoveryBarrier({ +- confirmShellForeground: async () => (await opts.subprocess.confirmShellForeground?.()) ?? false, ++ confirmShellForeground: async () => (await subprocess.confirmShellForeground?.()) ?? false, + release: (emission) => output.emit(emission), +- isAlive: opts.isAlive ++ isAlive + }) + barrier = recoveryBarrier + return { output, recoveryBarrier } +diff --git a/src/main/daemon/terminal-host-session-create.ts b/src/main/daemon/terminal-host-session-create.ts +index 8f6833c3d9..fc4cc01088 100644 +--- a/src/main/daemon/terminal-host-session-create.ts ++++ b/src/main/daemon/terminal-host-session-create.ts +@@ -150,7 +150,11 @@ async function spawnAndPublishSession( + historySeedChunks: opts.historySeedChunks, + ...(opts.startupIngress ? { startupIngress: opts.startupIngress } : {}), + wslDistro, +- onExit: () => deps.onSessionExit(opts.sessionId, opts.agentSessionGeneration), ++ onExit: createSessionExitHandler( ++ deps.onSessionExit, ++ opts.sessionId, ++ opts.agentSessionGeneration ++ ), + ...(deps.reportReadinessEvent ? { reportReadinessEvent: deps.reportReadinessEvent } : {}), + ...(opts.shellReadyTimeoutMs !== undefined + ? { shellReadyTimeoutMs: opts.shellReadyTimeoutMs } +@@ -212,6 +216,14 @@ async function spawnAndPublishSession( + } + } + ++function createSessionExitHandler( ++ onSessionExit: TerminalHostSessionCreateDependencies['onSessionExit'], ++ sessionId: string, ++ generation: string | undefined ++): () => void { ++ return () => onSessionExit(sessionId, generation) ++} ++ + // Why R_OK|X_OK: listing a directory needs read, and entering it needs search — both are what + // TCC withholds. A non-permission failure (ENOENT, ENOTDIR) reads as readable so it can never + // masquerade as a permission denial. +diff --git a/src/main/daemon/terminal-host.ts b/src/main/daemon/terminal-host.ts +index 81dae092b1..1fdbb6d161 100644 +--- a/src/main/daemon/terminal-host.ts ++++ b/src/main/daemon/terminal-host.ts +@@ -54,7 +54,6 @@ export class TerminalHost { + private onSessionReaped: TerminalHostOptions['onSessionReaped'] + private reportReadinessEvent: TerminalHostOptions['reportReadinessEvent'] + private onFinalCheckpoint: TerminalHostOptions['onFinalCheckpoint'] +- private maxTombstones: number + private creationFenced = false + private disposePromise: Promise | null = null + private readonly agentSessionOwners = new ClaimedAgentPtyOwnerRegistry() +@@ -71,8 +70,7 @@ export class TerminalHost { + this.onSessionReaped = opts.onSessionReaped + this.reportReadinessEvent = opts.reportReadinessEvent + this.onFinalCheckpoint = opts.onFinalCheckpoint +- this.maxTombstones = opts.maxTombstones ?? DEFAULT_MAX_TOMBSTONES +- this.killedTombstones = new TerminalHostTombstones(this.maxTombstones) ++ this.killedTombstones = new TerminalHostTombstones(opts.maxTombstones ?? DEFAULT_MAX_TOMBSTONES) + } + + async createOrAttach(opts: InternalCreateOrAttachOptions): Promise { +@@ -123,20 +121,7 @@ export class TerminalHost { + ...(this.reportReadinessEvent + ? { reportReadinessEvent: this.reportReadinessEvent } + : {}), +- onSessionExit: (sessionId, generation) => { +- const session = this.sessions.get(sessionId) +- if (session) { +- pruneRetiredPtyIncarnations(this.retiredIncarnations) +- this.retiredIncarnations.set(sessionId, { +- incarnationId: session.incarnationId, +- code: session.exitCode ?? 0, +- expiresAt: Date.now() + REMOTE_FOREGROUND_TOMBSTONE_RETENTION_MS +- }) +- } +- this.agentSessionOwners.release(sessionId, generation) +- this.agentSessionGenerations.forget(sessionId, generation) +- this.reapSession(sessionId) +- } ++ onSessionExit: this.handleSessionExit.bind(this) + }) + } + }) +@@ -146,6 +131,21 @@ export class TerminalHost { + } + } + ++ private handleSessionExit(sessionId: string, generation: string | undefined): void { ++ const session = this.sessions.get(sessionId) ++ if (session) { ++ pruneRetiredPtyIncarnations(this.retiredIncarnations) ++ this.retiredIncarnations.set(sessionId, { ++ incarnationId: session.incarnationId, ++ code: session.exitCode ?? 0, ++ expiresAt: Date.now() + REMOTE_FOREGROUND_TOMBSTONE_RETENTION_MS ++ }) ++ } ++ this.agentSessionOwners.release(sessionId, generation) ++ this.agentSessionGenerations.forget(sessionId, generation) ++ this.reapSession(sessionId) ++ } ++ + private assertCreateOrAttachAllowed(opts: InternalCreateOrAttachOptions): void { + if (this.creationFenced) { + throw new Error('Terminal host is shutting down') diff --git a/docs/audits/terminal-completed-spawn-inputs/mapped-admission-electron.json b/docs/audits/terminal-completed-spawn-inputs/mapped-admission-electron.json new file mode 100644 index 00000000000..788cde40243 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/mapped-admission-electron.json @@ -0,0 +1,84 @@ +{ + "node": "v24.21.0", + "electron": "43.7.0", + "v8": "15.0.245.31-electron.0", + "sourceMode": "mapped modules with working-tree dependencies", + "sourceHashes": { + "src/main/daemon/session-output-pipeline.ts": { + "baseline": "99ff9d936469a59e07f979b19344815c8636cd58a985e139614f221e99eaf8c9", + "fixed": "94f6f221d85388274d585ad714fda25c32a824942950074e4e95b85c94a6771f" + }, + "src/main/daemon/terminal-host-session-create.ts": { + "baseline": "44fb82244a3307b5c89ebf724640be585dfe1b79566d11649b38eaf518c54d32", + "fixed": "d54cbaff13c8daba1766e4fec24a6903ecdf1486249f1a9e5d96a207de410309" + }, + "src/main/daemon/terminal-host.ts": { + "baseline": "5216562b3c10c5fce4238614dc3a81887c970359a60e7e23179c6dfa15ccbcca", + "fixed": "f22856504559a6bb78498c6d5aae07cbbd80a21019723278a560628e0142236f" + }, + "src/main/daemon/daemon-terminal-admission.ts": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "src/main/daemon/daemon-pty-spawn-preparations.ts": "77c94466578a5066af069e45eb5e87a64b73b88580a13b2172b2ea846b434f4e" + }, + "phases": { + "baseline": { + "attached": { + "request": false, + "payload": false, + "preparation": true, + "options": true, + "env": true, + "history": true, + "signal": true + }, + "detached": { + "request": false, + "payload": false, + "preparation": true, + "options": true, + "env": true, + "history": true, + "signal": true + }, + "disposed": { + "request": false, + "payload": false, + "preparation": false, + "options": false, + "env": false, + "history": false, + "signal": false + }, + "historyVisibleAfterCollection": true + }, + "fixed": { + "attached": { + "request": false, + "payload": false, + "preparation": true, + "options": false, + "env": false, + "history": false, + "signal": true + }, + "detached": { + "request": false, + "payload": false, + "preparation": false, + "options": false, + "env": false, + "history": false, + "signal": false + }, + "disposed": { + "request": false, + "payload": false, + "preparation": false, + "options": false, + "env": false, + "history": false, + "signal": false + }, + "historyVisibleAfterCollection": true + } + } +} diff --git a/docs/audits/terminal-completed-spawn-inputs/mapped-admission-node.json b/docs/audits/terminal-completed-spawn-inputs/mapped-admission-node.json new file mode 100644 index 00000000000..a2ec9a4d177 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/mapped-admission-node.json @@ -0,0 +1,84 @@ +{ + "node": "v26.6.0", + "electron": null, + "v8": "14.6.202.34-node.26", + "sourceMode": "mapped modules with working-tree dependencies", + "sourceHashes": { + "src/main/daemon/session-output-pipeline.ts": { + "baseline": "99ff9d936469a59e07f979b19344815c8636cd58a985e139614f221e99eaf8c9", + "fixed": "94f6f221d85388274d585ad714fda25c32a824942950074e4e95b85c94a6771f" + }, + "src/main/daemon/terminal-host-session-create.ts": { + "baseline": "44fb82244a3307b5c89ebf724640be585dfe1b79566d11649b38eaf518c54d32", + "fixed": "d54cbaff13c8daba1766e4fec24a6903ecdf1486249f1a9e5d96a207de410309" + }, + "src/main/daemon/terminal-host.ts": { + "baseline": "5216562b3c10c5fce4238614dc3a81887c970359a60e7e23179c6dfa15ccbcca", + "fixed": "f22856504559a6bb78498c6d5aae07cbbd80a21019723278a560628e0142236f" + }, + "src/main/daemon/daemon-terminal-admission.ts": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "src/main/daemon/daemon-pty-spawn-preparations.ts": "77c94466578a5066af069e45eb5e87a64b73b88580a13b2172b2ea846b434f4e" + }, + "phases": { + "baseline": { + "attached": { + "request": false, + "payload": false, + "preparation": true, + "options": true, + "env": true, + "history": true, + "signal": true + }, + "detached": { + "request": false, + "payload": false, + "preparation": true, + "options": true, + "env": true, + "history": true, + "signal": true + }, + "disposed": { + "request": false, + "payload": false, + "preparation": false, + "options": false, + "env": false, + "history": false, + "signal": false + }, + "historyVisibleAfterCollection": true + }, + "fixed": { + "attached": { + "request": false, + "payload": false, + "preparation": true, + "options": false, + "env": false, + "history": false, + "signal": true + }, + "detached": { + "request": false, + "payload": false, + "preparation": false, + "options": false, + "env": false, + "history": false, + "signal": false + }, + "disposed": { + "request": false, + "payload": false, + "preparation": false, + "options": false, + "env": false, + "history": false, + "signal": false + }, + "historyVisibleAfterCollection": true + } + } +} diff --git a/docs/audits/terminal-completed-spawn-inputs/mapped-electron-baseline.json b/docs/audits/terminal-completed-spawn-inputs/mapped-electron-baseline.json new file mode 100644 index 00000000000..90de8b8ec0c --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/mapped-electron-baseline.json @@ -0,0 +1,66 @@ +{ + "node": "v24.21.0", + "electron": "43.7.0", + "v8": "15.0.245.31-electron.0", + "fixed": false, + "sourceMode": "mapped modules with working-tree dependencies", + "sourceHashes": { + "src/main/daemon/session-output-pipeline.ts": "99ff9d936469a59e07f979b19344815c8636cd58a985e139614f221e99eaf8c9", + "src/main/daemon/terminal-host-session-create.ts": "44fb82244a3307b5c89ebf724640be585dfe1b79566d11649b38eaf518c54d32", + "src/main/daemon/terminal-host.ts": "5216562b3c10c5fce4238614dc3a81887c970359a60e7e23179c6dfa15ccbcca" + }, + "reports": [ + { + "case": "completed-inputs", + "whileLive": { + "options": 3, + "env": 3, + "history": 3, + "signal": 3 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "liveSessionCountAtCollection": 3 + }, + { + "case": "pending-inputs", + "duringSpawn": { + "options": 1, + "env": 1, + "history": 1, + "signal": 1 + }, + "afterPublication": { + "options": 1, + "env": 1, + "history": 1, + "signal": 1 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + } + }, + { + "case": "exit-and-recreate", + "exitVerdict": "exited", + "exitReason": "pty_exit_7", + "reaped": ["claimed", "claimed"], + "newIncarnation": true, + "newGeneration": true + }, + { + "case": "foreground-confirmation", + "confirmations": 1, + "preservedReceiver": true, + "owner": "shell", + "queuedPromptReleased": true + } + ] +} diff --git a/docs/audits/terminal-completed-spawn-inputs/mapped-electron-fixed.json b/docs/audits/terminal-completed-spawn-inputs/mapped-electron-fixed.json new file mode 100644 index 00000000000..653e5d8cd75 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/mapped-electron-fixed.json @@ -0,0 +1,66 @@ +{ + "node": "v24.21.0", + "electron": "43.7.0", + "v8": "15.0.245.31-electron.0", + "fixed": true, + "sourceMode": "mapped modules with working-tree dependencies", + "sourceHashes": { + "src/main/daemon/session-output-pipeline.ts": "94f6f221d85388274d585ad714fda25c32a824942950074e4e95b85c94a6771f", + "src/main/daemon/terminal-host-session-create.ts": "d54cbaff13c8daba1766e4fec24a6903ecdf1486249f1a9e5d96a207de410309", + "src/main/daemon/terminal-host.ts": "f22856504559a6bb78498c6d5aae07cbbd80a21019723278a560628e0142236f" + }, + "reports": [ + { + "case": "completed-inputs", + "whileLive": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "liveSessionCountAtCollection": 3 + }, + { + "case": "pending-inputs", + "duringSpawn": { + "options": 1, + "env": 1, + "history": 1, + "signal": 1 + }, + "afterPublication": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + } + }, + { + "case": "exit-and-recreate", + "exitVerdict": "exited", + "exitReason": "pty_exit_7", + "reaped": ["claimed", "claimed"], + "newIncarnation": true, + "newGeneration": true + }, + { + "case": "foreground-confirmation", + "confirmations": 1, + "preservedReceiver": true, + "owner": "shell", + "queuedPromptReleased": true + } + ] +} diff --git a/docs/audits/terminal-completed-spawn-inputs/mapped-node-baseline.json b/docs/audits/terminal-completed-spawn-inputs/mapped-node-baseline.json new file mode 100644 index 00000000000..b77daa28825 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/mapped-node-baseline.json @@ -0,0 +1,66 @@ +{ + "node": "v26.6.0", + "electron": null, + "v8": "14.6.202.34-node.26", + "fixed": false, + "sourceMode": "mapped modules with working-tree dependencies", + "sourceHashes": { + "src/main/daemon/session-output-pipeline.ts": "99ff9d936469a59e07f979b19344815c8636cd58a985e139614f221e99eaf8c9", + "src/main/daemon/terminal-host-session-create.ts": "44fb82244a3307b5c89ebf724640be585dfe1b79566d11649b38eaf518c54d32", + "src/main/daemon/terminal-host.ts": "5216562b3c10c5fce4238614dc3a81887c970359a60e7e23179c6dfa15ccbcca" + }, + "reports": [ + { + "case": "completed-inputs", + "whileLive": { + "options": 3, + "env": 3, + "history": 3, + "signal": 3 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "liveSessionCountAtCollection": 3 + }, + { + "case": "pending-inputs", + "duringSpawn": { + "options": 1, + "env": 1, + "history": 1, + "signal": 1 + }, + "afterPublication": { + "options": 1, + "env": 1, + "history": 1, + "signal": 1 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + } + }, + { + "case": "exit-and-recreate", + "exitVerdict": "exited", + "exitReason": "pty_exit_7", + "reaped": ["claimed", "claimed"], + "newIncarnation": true, + "newGeneration": true + }, + { + "case": "foreground-confirmation", + "confirmations": 1, + "preservedReceiver": true, + "owner": "shell", + "queuedPromptReleased": true + } + ] +} diff --git a/docs/audits/terminal-completed-spawn-inputs/mapped-node-fixed.json b/docs/audits/terminal-completed-spawn-inputs/mapped-node-fixed.json new file mode 100644 index 00000000000..571ae217572 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/mapped-node-fixed.json @@ -0,0 +1,66 @@ +{ + "node": "v26.6.0", + "electron": null, + "v8": "14.6.202.34-node.26", + "fixed": true, + "sourceMode": "mapped modules with working-tree dependencies", + "sourceHashes": { + "src/main/daemon/session-output-pipeline.ts": "94f6f221d85388274d585ad714fda25c32a824942950074e4e95b85c94a6771f", + "src/main/daemon/terminal-host-session-create.ts": "d54cbaff13c8daba1766e4fec24a6903ecdf1486249f1a9e5d96a207de410309", + "src/main/daemon/terminal-host.ts": "f22856504559a6bb78498c6d5aae07cbbd80a21019723278a560628e0142236f" + }, + "reports": [ + { + "case": "completed-inputs", + "whileLive": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "liveSessionCountAtCollection": 3 + }, + { + "case": "pending-inputs", + "duringSpawn": { + "options": 1, + "env": 1, + "history": 1, + "signal": 1 + }, + "afterPublication": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + } + }, + { + "case": "exit-and-recreate", + "exitVerdict": "exited", + "exitReason": "pty_exit_7", + "reaped": ["claimed", "claimed"], + "newIncarnation": true, + "newGeneration": true + }, + { + "case": "foreground-confirmation", + "confirmations": 1, + "preservedReceiver": true, + "owner": "shell", + "queuedPromptReleased": true + } + ] +} diff --git a/docs/audits/terminal-completed-spawn-inputs/node-baseline.json b/docs/audits/terminal-completed-spawn-inputs/node-baseline.json new file mode 100644 index 00000000000..4a7048adb29 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/node-baseline.json @@ -0,0 +1,66 @@ +{ + "node": "v26.6.0", + "electron": null, + "v8": "14.6.202.34-node.26", + "fixed": false, + "sourceMode": "working-tree modules and dependencies", + "sourceHashes": { + "src/main/daemon/session-output-pipeline.ts": "99ff9d936469a59e07f979b19344815c8636cd58a985e139614f221e99eaf8c9", + "src/main/daemon/terminal-host-session-create.ts": "44fb82244a3307b5c89ebf724640be585dfe1b79566d11649b38eaf518c54d32", + "src/main/daemon/terminal-host.ts": "8cd6804b249ffb0d82da7f5a7ec8faa66514b0d6e3e36472cd81e6409c3edbc4" + }, + "reports": [ + { + "case": "completed-inputs", + "whileLive": { + "options": 3, + "env": 3, + "history": 3, + "signal": 3 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "liveSessionCountAtCollection": 3 + }, + { + "case": "pending-inputs", + "duringSpawn": { + "options": 1, + "env": 1, + "history": 1, + "signal": 1 + }, + "afterPublication": { + "options": 1, + "env": 1, + "history": 1, + "signal": 1 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + } + }, + { + "case": "exit-and-recreate", + "exitVerdict": "exited", + "exitReason": "pty_exit_7", + "reaped": ["claimed", "claimed"], + "newIncarnation": true, + "newGeneration": true + }, + { + "case": "foreground-confirmation", + "confirmations": 1, + "preservedReceiver": true, + "owner": "shell", + "queuedPromptReleased": true + } + ] +} diff --git a/docs/audits/terminal-completed-spawn-inputs/node-fixed.json b/docs/audits/terminal-completed-spawn-inputs/node-fixed.json new file mode 100644 index 00000000000..b2cf00a4556 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/node-fixed.json @@ -0,0 +1,66 @@ +{ + "node": "v26.6.0", + "electron": null, + "v8": "14.6.202.34-node.26", + "fixed": true, + "sourceMode": "working-tree modules and dependencies", + "sourceHashes": { + "src/main/daemon/session-output-pipeline.ts": "94f6f221d85388274d585ad714fda25c32a824942950074e4e95b85c94a6771f", + "src/main/daemon/terminal-host-session-create.ts": "d54cbaff13c8daba1766e4fec24a6903ecdf1486249f1a9e5d96a207de410309", + "src/main/daemon/terminal-host.ts": "23cfd9c6317db30edd48a0c8bd7c54658206654703461cf12295274b23fb8844" + }, + "reports": [ + { + "case": "completed-inputs", + "whileLive": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "liveSessionCountAtCollection": 3 + }, + { + "case": "pending-inputs", + "duringSpawn": { + "options": 1, + "env": 1, + "history": 1, + "signal": 1 + }, + "afterPublication": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + }, + "afterDispose": { + "options": 0, + "env": 0, + "history": 0, + "signal": 0 + } + }, + { + "case": "exit-and-recreate", + "exitVerdict": "exited", + "exitReason": "pty_exit_7", + "reaped": ["claimed", "claimed"], + "newIncarnation": true, + "newGeneration": true + }, + { + "case": "foreground-confirmation", + "confirmations": 1, + "preservedReceiver": true, + "owner": "shell", + "queuedPromptReleased": true + } + ] +} diff --git a/docs/audits/terminal-completed-spawn-inputs/reproduce.cjs b/docs/audits/terminal-completed-spawn-inputs/reproduce.cjs new file mode 100644 index 00000000000..4900a3f4b5a --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/reproduce.cjs @@ -0,0 +1,208 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const { load, evaluatedSourceHashes, sourceMode, reportPrefix } = require('./spawn-source.cjs') +const { + subprocess, + streamClient, + startWithInputs, + counts, + expected, + collect +} = require('./spawn-fixture.cjs') +const fixed = !process.argv.includes('--baseline') +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function') + +async function completedInputs(Host) { + const host = new Host({ spawnSubprocess: async () => subprocess() }) + const refs = [] + try { + for (let index = 0; index < 3; index += 1) { + const created = startWithInputs(host, `retention-${index}`) + assert.equal((await created.creation).historySeeded, true) + refs.push(created.refs) + } + await collect() + const whileLive = counts(refs) + assert.deepEqual(whileLive, expected(fixed ? 0 : 3)) + assert.equal(host.listSessions().length, 3) + assert.ok(host.getSnapshot('retention-0').snapshotAnsi.includes('retention-seed')) + await host.dispose() + await collect() + const afterDispose = counts(refs) + assert.deepEqual(afterDispose, expected(0)) + return { case: 'completed-inputs', whileLive, afterDispose, liveSessionCountAtCollection: 3 } + } finally { + await host.dispose() + } +} + +async function pendingInputs(Host) { + const gate = Promise.withResolvers() + const host = new Host({ + spawnSubprocess: async () => { + await gate.promise + return subprocess() + } + }) + const created = startWithInputs(host, 'pending') + try { + await collect() + const duringSpawn = counts([created.refs]) + assert.deepEqual(duringSpawn, expected(1)) + gate.resolve() + assert.equal((await created.creation).isNew, true) + await collect() + const afterPublication = counts([created.refs]) + assert.deepEqual(afterPublication, expected(fixed ? 0 : 1)) + await host.dispose() + await collect() + assert.deepEqual(counts([created.refs]), expected(0)) + return { + case: 'pending-inputs', + duringSpawn, + afterPublication, + afterDispose: counts([created.refs]) + } + } finally { + gate.resolve() + await created.creation + await host.dispose() + } +} + +async function exitAndRecreate(Host) { + const handles = [] + const reaped = [] + const host = new Host({ + spawnSubprocess: async () => { + const handle = subprocess() + handles.push(handle) + return handle + }, + onSessionReaped: (id) => reaped.push(id) + }) + const options = { + sessionId: 'claimed', + cols: 80, + rows: 24, + streamClient, + agentSessionEnsure: { + claim: { + digestVersion: 1, + keyId: 'key', + identityDigest: 'a'.repeat(43), + worktreeScopeDigest: 'b'.repeat(43), + agent: 'codex' + }, + surface: { + worktreeId: 'worktree', + tabId: 'tab', + leafId: '11111111-1111-4111-8111-111111111111', + terminalHandle: 'term_claimed' + } + } + } + try { + const first = await host.createOrAttach(options) + handles[0].emitExit(7) + assert.deepEqual(reaped, ['claimed']) + assert.deepEqual(host.listSessions(), []) + const evidence = ( + await host.inspectProcess('claimed', { expectedIncarnationId: first.incarnationId }) + ).foregroundProcessEvidence + assert.equal(evidence.verdict, 'exited') + assert.equal(evidence.reason, 'pty_exit_7') + assert.equal(evidence.ptyIncarnationId, first.incarnationId) + const second = await host.createOrAttach(options) + assert.equal(second.agentSessionEnsure.disposition, 'created') + assert.notEqual( + second.agentSessionEnsure.owner.generation, + first.agentSessionEnsure.owner.generation + ) + assert.notEqual(second.incarnationId, first.incarnationId) + assert.equal(handles.length, 2) + await host.dispose() + assert.deepEqual(reaped, ['claimed', 'claimed']) + return { + case: 'exit-and-recreate', + exitVerdict: evidence.verdict, + exitReason: evidence.reason, + reaped, + newIncarnation: true, + newGeneration: true + } + } finally { + await host.dispose() + } +} + +async function foregroundConfirmation(Host) { + const gate = Promise.withResolvers() + let confirmations = 0 + const handle = { + ...subprocess(), + confirmShellForeground() { + assert.equal(this, handle) + confirmations += 1 + return gate.promise + } + } + const host = new Host({ spawnSubprocess: async () => handle }) + try { + await host.createOrAttach({ sessionId: 'recovery', cols: 80, rows: 24, streamClient }) + handle.emitData('\x1b[?1049hTUI\x1b]133;D;137\x07SHELL-PROMPT') + await new Promise((resolve) => setImmediate(resolve)) + assert.equal(confirmations, 1) + gate.resolve(true) + const snapshot = await host.getSettledSnapshot('recovery') + assert.equal(snapshot.terminalOwner, 'shell') + assert.ok(snapshot.snapshotAnsi.includes('SHELL-PROMPT')) + return { + case: 'foreground-confirmation', + confirmations, + preservedReceiver: true, + owner: snapshot.terminalOwner, + queuedPromptReleased: true + } + } finally { + gate.resolve(false) + await host.dispose() + } +} + +async function main() { + const Host = await load(fixed) + const reports = [ + await completedInputs(Host), + await pendingInputs(Host), + await exitAndRecreate(Host), + await foregroundConfirmation(Host) + ] + const report = { + node: process.version, + electron: process.versions.electron ?? null, + v8: process.versions.v8, + fixed, + sourceMode, + sourceHashes: Object.fromEntries( + Object.entries(evaluatedSourceHashes).map(([file, hashes]) => [ + file, + fixed ? hashes.fixed : hashes.baseline + ]) + ), + reports + } + const file = `${reportPrefix}${process.versions.electron ? 'electron' : 'node'}-${fixed ? 'fixed' : 'baseline'}.json` + fs.writeFileSync(path.join(__dirname, file), `${JSON.stringify(report, null, 2)}\n`) + console.log(JSON.stringify(report, null, 2)) +} +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) +setTimeout(() => { + console.error('fixture timeout') + process.exit(2) +}, 15000).unref() diff --git a/docs/audits/terminal-completed-spawn-inputs/source-versions.json b/docs/audits/terminal-completed-spawn-inputs/source-versions.json new file mode 100644 index 00000000000..bdfb76e1493 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/source-versions.json @@ -0,0 +1,113 @@ +{ + "baselineCommit": "9e2c137548bf99f91255ab4862c01145e42a0883", + "sources": [ + { + "sourcePath": "src/main/daemon/session-output-pipeline.ts", + "baselineSha256": "99ff9d936469a59e07f979b19344815c8636cd58a985e139614f221e99eaf8c9", + "fixedSha256": "94f6f221d85388274d585ad714fda25c32a824942950074e4e95b85c94a6771f" + }, + { + "sourcePath": "src/main/daemon/terminal-host-session-create.ts", + "baselineSha256": "44fb82244a3307b5c89ebf724640be585dfe1b79566d11649b38eaf518c54d32", + "fixedSha256": "d54cbaff13c8daba1766e4fec24a6903ecdf1486249f1a9e5d96a207de410309" + }, + { + "sourcePath": "src/main/daemon/terminal-host.ts", + "baselineSha256": "8cd6804b249ffb0d82da7f5a7ec8faa66514b0d6e3e36472cd81e6409c3edbc4", + "fixedSha256": "23cfd9c6317db30edd48a0c8bd7c54658206654703461cf12295274b23fb8844", + "alternatePairs": [ + { + "name": "independent-main-publication", + "baselineSha256": "5216562b3c10c5fce4238614dc3a81887c970359a60e7e23179c6dfa15ccbcca", + "fixedSha256": "f22856504559a6bb78498c6d5aae07cbbd80a21019723278a560628e0142236f" + } + ] + } + ], + "comparedRefs": [ + { + "ref": "origin/main", + "commit": "77cd61df396f25ec91ee2d5ddcbd1f55aa94f818", + "sources": [ + { + "sourcePath": "src/main/daemon/session-output-pipeline.ts", + "sha256": "99ff9d936469a59e07f979b19344815c8636cd58a985e139614f221e99eaf8c9", + "matchesBaseline": true, + "patchApplies": true, + "overlaySha256": "94f6f221d85388274d585ad714fda25c32a824942950074e4e95b85c94a6771f", + "matchesFixed": true + }, + { + "sourcePath": "src/main/daemon/terminal-host-session-create.ts", + "sha256": "44fb82244a3307b5c89ebf724640be585dfe1b79566d11649b38eaf518c54d32", + "matchesBaseline": true, + "patchApplies": true, + "overlaySha256": "d54cbaff13c8daba1766e4fec24a6903ecdf1486249f1a9e5d96a207de410309", + "matchesFixed": true + }, + { + "sourcePath": "src/main/daemon/terminal-host.ts", + "sha256": "5216562b3c10c5fce4238614dc3a81887c970359a60e7e23179c6dfa15ccbcca", + "matchesBaseline": false, + "patchApplies": true, + "overlaySha256": "f22856504559a6bb78498c6d5aae07cbbd80a21019723278a560628e0142236f", + "matchesFixed": false + } + ] + }, + { + "ref": "v1.4.198", + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sources": [ + { + "sourcePath": "src/main/daemon/session-output-pipeline.ts", + "sha256": "99ff9d936469a59e07f979b19344815c8636cd58a985e139614f221e99eaf8c9", + "matchesBaseline": true, + "patchApplies": true, + "overlaySha256": "94f6f221d85388274d585ad714fda25c32a824942950074e4e95b85c94a6771f", + "matchesFixed": true + }, + { + "sourcePath": "src/main/daemon/terminal-host-session-create.ts", + "sha256": "44fb82244a3307b5c89ebf724640be585dfe1b79566d11649b38eaf518c54d32", + "matchesBaseline": true, + "patchApplies": true, + "overlaySha256": "d54cbaff13c8daba1766e4fec24a6903ecdf1486249f1a9e5d96a207de410309", + "matchesFixed": true + }, + { + "sourcePath": "src/main/daemon/terminal-host.ts", + "sha256": "5216562b3c10c5fce4238614dc3a81887c970359a60e7e23179c6dfa15ccbcca", + "matchesBaseline": false, + "patchApplies": true, + "overlaySha256": "f22856504559a6bb78498c6d5aae07cbbd80a21019723278a560628e0142236f", + "matchesFixed": false + } + ] + } + ], + "publicationMain": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sources": [ + { + "sourcePath": "src/main/daemon/session-output-pipeline.ts", + "baselineSha256": "99ff9d936469a59e07f979b19344815c8636cd58a985e139614f221e99eaf8c9", + "fixedSha256": "94f6f221d85388274d585ad714fda25c32a824942950074e4e95b85c94a6771f", + "patchApplies": true + }, + { + "sourcePath": "src/main/daemon/terminal-host-session-create.ts", + "baselineSha256": "44fb82244a3307b5c89ebf724640be585dfe1b79566d11649b38eaf518c54d32", + "fixedSha256": "d54cbaff13c8daba1766e4fec24a6903ecdf1486249f1a9e5d96a207de410309", + "patchApplies": true + }, + { + "sourcePath": "src/main/daemon/terminal-host.ts", + "baselineSha256": "5216562b3c10c5fce4238614dc3a81887c970359a60e7e23179c6dfa15ccbcca", + "fixedSha256": "f22856504559a6bb78498c6d5aae07cbbd80a21019723278a560628e0142236f", + "patchApplies": true + } + ], + "dependencyScope": "Only these three modules are mapped; other dependencies are current worktree source." + } +} diff --git a/docs/audits/terminal-completed-spawn-inputs/spawn-fixture.cjs b/docs/audits/terminal-completed-spawn-inputs/spawn-fixture.cjs new file mode 100644 index 00000000000..193ab304a33 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/spawn-fixture.cjs @@ -0,0 +1,81 @@ +const assert = require('node:assert/strict') + +function subprocess() { + let dataListener + let exitListener + return { + pid: 424242, + getForegroundProcess: () => null, + write() {}, + resize() {}, + signal() {}, + kill() { + exitListener?.(0) + }, + forceKill() { + exitListener?.(137) + }, + terminateOwnedTree: () => 'unavailable', + onData(listener) { + dataListener = listener + }, + onExit(listener) { + exitListener = listener + }, + dispose() { + dataListener = undefined + exitListener = undefined + }, + emitData(data) { + dataListener?.(data) + }, + emitExit(code) { + exitListener?.(code) + } + } +} + +// These callbacks must not share a lexical context with the request's signal. +const streamClient = { onData() {}, onExit() {} } +function startWithInputs(host, sessionId) { + const controller = new AbortController() + const env = { RETENTION_FIXTURE: 'x'.repeat(1024) } + const historySeedChunks = ['retention-seed\r\n'] + const options = { + sessionId, + cols: 80, + rows: 24, + env, + historySeedChunks, + streamClient, + cancelSignal: controller.signal, + isCanceled: () => controller.signal.aborted + } + return { + refs: { + options: new WeakRef(options), + env: new WeakRef(env), + history: new WeakRef(historySeedChunks), + signal: new WeakRef(controller.signal) + }, + creation: host.createOrAttach(options) + } +} + +function counts(refs) { + return Object.fromEntries( + ['options', 'env', 'history', 'signal'].map((key) => [ + key, + refs.filter((ref) => ref[key].deref() !== undefined).length + ]) + ) +} +const expected = (count) => ({ options: count, env: count, history: count, signal: count }) +async function collect() { + assert.equal(typeof global.gc, 'function') + for (let round = 0; round < 4; round += 1) { + await new Promise((resolve) => setImmediate(resolve)) + global.gc() + } +} +module.exports = { subprocess, streamClient, startWithInputs, counts, expected, collect } diff --git a/docs/audits/terminal-completed-spawn-inputs/spawn-source.cjs b/docs/audits/terminal-completed-spawn-inputs/spawn-source.cjs new file mode 100644 index 00000000000..5001b5d4d31 --- /dev/null +++ b/docs/audits/terminal-completed-spawn-inputs/spawn-source.cjs @@ -0,0 +1,114 @@ +const assert = require('node:assert/strict') +const { createHash } = require('node:crypto') +const { readFileSync } = require('node:fs') +const path = require('node:path') +const Module = require('node:module') +const esbuild = require('esbuild') +const { applyPatch, parsePatch, reversePatch } = require('diff') +const versions = require('./source-versions.json') + +const root = path.resolve(__dirname, '../../..') +const readText = (file) => readFileSync(file, 'utf8').replace(/\r\n/g, '\n') +const sha = (value) => createHash('sha256').update(value).digest('hex') +const patches = parsePatch(readText(path.join(__dirname, 'fix.patch'))) +assert.equal(patches.length, versions.sources.length) +const fixedSources = new Map() +const baselineSources = new Map() +const evaluatedSourceHashes = {} +const sourceMapPath = process.env.ORCA_SPAWN_INPUT_PROOF_SOURCE_MAP +const sourceOverrides = sourceMapPath ? JSON.parse(readText(path.resolve(sourceMapPath))) : null +if (sourceMapPath) { + assert.equal(typeof sourceOverrides, 'object') + assert.notEqual(sourceOverrides, null) + assert.equal(Array.isArray(sourceOverrides), false) + assert.deepEqual( + Object.keys(sourceOverrides).sort(), + versions.sources.map((source) => source.sourcePath).sort() + ) +} +for (const source of versions.sources) { + const file = path.join(root, source.sourcePath) + const fixed = sourceOverrides ? sourceOverrides[source.sourcePath] : readText(file) + assert.equal(typeof fixed, 'string') + const pair = [source, ...(source.alternatePairs ?? [])].find( + (entry) => entry.fixedSha256 === sha(fixed) + ) + assert.ok(pair, `Unreviewed product source: ${source.sourcePath}`) + const patch = patches.find((entry) => entry.oldFileName === `a/${source.sourcePath}`) + assert.ok(patch) + const baseline = applyPatch(fixed, reversePatch(patch)) + assert.notEqual(baseline, false) + assert.equal(sha(baseline), pair.baselineSha256, `Baseline changed: ${source.sourcePath}`) + fixedSources.set(file, fixed) + baselineSources.set(file, baseline) + evaluatedSourceHashes[source.sourcePath] = { baseline: sha(baseline), fixed: sha(fixed) } +} + +const sourceMode = sourceMapPath + ? 'mapped modules with working-tree dependencies' + : 'working-tree modules and dependencies' +const reportPrefix = sourceMapPath ? 'mapped-' : '' + +async function loadExports(fixed) { + const sources = fixed ? fixedSources : baselineSources + const build = await esbuild.build({ + stdin: { + contents: [ + "export { TerminalHost } from './src/main/daemon/terminal-host'", + "export { DaemonTerminalAdmission } from './src/main/daemon/daemon-terminal-admission'", + "export { DaemonPtySpawnPreparations } from './src/main/daemon/daemon-pty-spawn-preparations'" + ].join(';'), + resolveDir: root, + loader: 'ts' + }, + platform: 'node', + format: 'cjs', + bundle: true, + packages: 'external', + write: false, + plugins: [ + { + name: 'reviewed-spawn-input-sources', + setup(builder) { + builder.onLoad( + { filter: /(?:terminal-host(?:-session-create)?|session-output-pipeline)\.ts$/ }, + (args) => { + const contents = sources.get(args.path) + return contents === undefined ? undefined : { contents, loader: 'ts' } + } + ) + builder.onResolve({ filter: /pty-descendant-termination$/ }, () => ({ + path: 'no-os-signals', + namespace: 'fixture' + })) + builder.onLoad({ filter: /.*/, namespace: 'fixture' }, () => ({ + contents: + "export function killWithDescendantSweep() { throw new Error('Unexpected real process teardown') }", + loader: 'js' + })) + } + } + ] + }) + const filename = path.join(__dirname, 'bundled-terminal-host.cjs') + const loaded = new Module(filename, module) + loaded.filename = filename + loaded.paths = Module._nodeModulePaths(__dirname) + loaded._compile(build.outputFiles[0].text, filename) + return loaded.exports +} + +async function load(fixed) { + return (await loadExports(fixed)).TerminalHost +} + +module.exports = { + load, + loadExports, + versions, + sha, + baselineSources, + evaluatedSourceHashes, + sourceMode, + reportPrefix +} diff --git a/src/main/daemon/session-output-pipeline.ts b/src/main/daemon/session-output-pipeline.ts index c249e4d1d31..f67d2e15b92 100644 --- a/src/main/daemon/session-output-pipeline.ts +++ b/src/main/daemon/session-output-pipeline.ts @@ -14,6 +14,7 @@ export function createSessionOutputPipeline(opts: { subprocess: SubprocessHandle isAlive: () => boolean }): { output: SessionOutputPlane; recoveryBarrier: TerminalShellRecoveryBarrier } { + const { subprocess, isAlive } = opts let barrier: TerminalShellRecoveryBarrier | null = null const output = new SessionOutputPlane({ cols: opts.cols, @@ -24,9 +25,9 @@ export function createSessionOutputPipeline(opts: { getTerminalOwner: () => barrier?.getOwner() }) const recoveryBarrier = new TerminalShellRecoveryBarrier({ - confirmShellForeground: async () => (await opts.subprocess.confirmShellForeground?.()) ?? false, + confirmShellForeground: async () => (await subprocess.confirmShellForeground?.()) ?? false, release: (emission) => output.emit(emission), - isAlive: opts.isAlive + isAlive }) barrier = recoveryBarrier return { output, recoveryBarrier } diff --git a/src/main/daemon/terminal-host-session-create.ts b/src/main/daemon/terminal-host-session-create.ts index 8f6833c3d9f..fc4cc01088a 100644 --- a/src/main/daemon/terminal-host-session-create.ts +++ b/src/main/daemon/terminal-host-session-create.ts @@ -150,7 +150,11 @@ async function spawnAndPublishSession( historySeedChunks: opts.historySeedChunks, ...(opts.startupIngress ? { startupIngress: opts.startupIngress } : {}), wslDistro, - onExit: () => deps.onSessionExit(opts.sessionId, opts.agentSessionGeneration), + onExit: createSessionExitHandler( + deps.onSessionExit, + opts.sessionId, + opts.agentSessionGeneration + ), ...(deps.reportReadinessEvent ? { reportReadinessEvent: deps.reportReadinessEvent } : {}), ...(opts.shellReadyTimeoutMs !== undefined ? { shellReadyTimeoutMs: opts.shellReadyTimeoutMs } @@ -212,6 +216,14 @@ async function spawnAndPublishSession( } } +function createSessionExitHandler( + onSessionExit: TerminalHostSessionCreateDependencies['onSessionExit'], + sessionId: string, + generation: string | undefined +): () => void { + return () => onSessionExit(sessionId, generation) +} + // Why R_OK|X_OK: listing a directory needs read, and entering it needs search — both are what // TCC withholds. A non-permission failure (ENOENT, ENOTDIR) reads as readable so it can never // masquerade as a permission denial. diff --git a/src/main/daemon/terminal-host-spawn-input-retention.test.ts b/src/main/daemon/terminal-host-spawn-input-retention.test.ts new file mode 100644 index 00000000000..651fddcb0bf --- /dev/null +++ b/src/main/daemon/terminal-host-spawn-input-retention.test.ts @@ -0,0 +1,214 @@ +import { describe, expect, it, vi } from 'vitest' +import type { SubprocessHandle } from './session-subprocess-handle' +import type { InternalCreateOrAttachOptions } from './terminal-host-agent-session-claim' +import { TerminalHost } from './terminal-host' + +vi.mock('../pty-descendant-termination', () => ({ + killWithDescendantSweep: () => { + throw new Error('The retention fixture must not signal real processes') + } +})) + +function subprocess() { + let dataListener: ((data: string) => void) | undefined + let exitListener: ((code: number) => void) | undefined + return { + pid: 424242, + getForegroundProcess: () => null, + write() {}, + resize() {}, + signal() {}, + kill() { + exitListener?.(0) + }, + forceKill() { + exitListener?.(137) + }, + terminateOwnedTree: () => 'unavailable' as const, + onData(listener: (data: string) => void) { + dataListener = listener + }, + onExit(listener: (code: number) => void) { + exitListener = listener + }, + dispose() { + dataListener = undefined + exitListener = undefined + }, + emitData(data: string) { + dataListener?.(data) + }, + emitExit(code: number) { + exitListener?.(code) + } + } satisfies SubprocessHandle & { + emitData: (data: string) => void + emitExit: (code: number) => void + } +} + +const streamClient = { onData() {}, onExit() {} } + +function startWithInputs(host: TerminalHost, sessionId: string) { + const controller = new AbortController() + const env = { RETENTION_FIXTURE: 'x'.repeat(1024) } + const historySeedChunks = ['retention-seed\r\n'] + const options: InternalCreateOrAttachOptions = { + sessionId, + cols: 80, + rows: 24, + env, + historySeedChunks, + streamClient, + cancelSignal: controller.signal, + isCanceled: () => controller.signal.aborted + } + return { + refs: [ + new WeakRef(options), + new WeakRef(env), + new WeakRef(historySeedChunks), + new WeakRef(controller.signal) + ], + creation: host.createOrAttach(options) + } +} + +async function collect(): Promise { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + for (let round = 0; round < 4; round += 1) { + await new Promise((resolve) => setImmediate(resolve)) + globalThis.gc() + } +} + +describe('TerminalHost completed spawn inputs', () => { + it('releases request, environment, consumed history and cancellation inputs for live sessions', async () => { + const host = new TerminalHost({ spawnSubprocess: async () => subprocess() }) + try { + const refs: WeakRef[] = [] + for (let index = 0; index < 3; index += 1) { + const created = startWithInputs(host, `retention-${index}`) + expect((await created.creation).historySeeded).toBe(true) + refs.push(...created.refs) + } + await collect() + expect(refs.map((ref) => ref.deref() === undefined)).toEqual(Array(12).fill(true)) + expect(host.listSessions()).toHaveLength(3) + expect(host.getSnapshot('retention-0')?.snapshotAnsi).toContain('retention-seed') + } finally { + await host.dispose() + } + }) + + it('retains inputs during spawn and releases them after publication', async () => { + const gate = Promise.withResolvers() + const host = new TerminalHost({ + spawnSubprocess: async () => { + await gate.promise + return subprocess() + } + }) + const created = startWithInputs(host, 'pending') + try { + await collect() + expect(created.refs.map((ref) => ref.deref() !== undefined)).toEqual(Array(4).fill(true)) + gate.resolve() + expect((await created.creation).isNew).toBe(true) + await collect() + expect(created.refs.map((ref) => ref.deref() === undefined)).toEqual(Array(4).fill(true)) + expect(host.listSessions()).toHaveLength(1) + } finally { + gate.resolve() + await created.creation + await host.dispose() + } + }) + + it('reaps exited sessions, preserves exit evidence and releases claimed generations', async () => { + const handles: ReturnType[] = [] + const reaped: string[] = [] + const host = new TerminalHost({ + spawnSubprocess: async () => { + const handle = subprocess() + handles.push(handle) + return handle + }, + onSessionReaped: (sessionId) => reaped.push(sessionId) + }) + const options = { + sessionId: 'claimed', + cols: 80, + rows: 24, + streamClient, + agentSessionEnsure: { + claim: { + digestVersion: 1 as const, + keyId: 'key', + identityDigest: 'a'.repeat(43), + worktreeScopeDigest: 'b'.repeat(43), + agent: 'codex' as const + }, + surface: { + worktreeId: 'worktree', + tabId: 'tab', + leafId: '11111111-1111-4111-8111-111111111111', + terminalHandle: 'term_claimed' + } + } + } + try { + const first = await host.createOrAttach(options) + handles[0]?.emitExit(7) + expect(reaped).toEqual(['claimed']) + expect(host.listSessions()).toEqual([]) + expect( + await host.inspectProcess('claimed', { expectedIncarnationId: first.incarnationId }) + ).toMatchObject({ + foregroundProcessEvidence: { + verdict: 'exited', + reason: 'pty_exit_7', + ptyIncarnationId: first.incarnationId + } + }) + const second = await host.createOrAttach(options) + expect(second.agentSessionEnsure?.disposition).toBe('created') + expect(second.incarnationId).not.toBe(first.incarnationId) + expect(second.agentSessionEnsure?.owner.generation).not.toBe( + first.agentSessionEnsure?.owner.generation + ) + expect(handles).toHaveLength(2) + } finally { + await host.dispose() + } + expect(reaped).toEqual(['claimed', 'claimed']) + }) + + it('confirms shell recovery with the subprocess receiver and releases queued output', async () => { + let confirmations = 0 + const gate = Promise.withResolvers() + const handle = { + ...subprocess(), + confirmShellForeground() { + expect(this).toBe(handle) + confirmations += 1 + return gate.promise + } + } + const host = new TerminalHost({ spawnSubprocess: async () => handle }) + try { + await host.createOrAttach({ sessionId: 'recovery', cols: 80, rows: 24, streamClient }) + handle.emitData('\x1b[?1049hTUI\x1b]133;D;137\x07SHELL-PROMPT') + await vi.waitFor(() => expect(confirmations).toBe(1)) + gate.resolve(true) + const snapshot = await host.getSettledSnapshot('recovery') + expect(snapshot?.terminalOwner).toBe('shell') + expect(snapshot?.snapshotAnsi).toContain('SHELL-PROMPT') + } finally { + gate.resolve(false) + await host.dispose() + } + }) +}) diff --git a/src/main/daemon/terminal-host.ts b/src/main/daemon/terminal-host.ts index 95bedd1a7fd..9c164354564 100644 --- a/src/main/daemon/terminal-host.ts +++ b/src/main/daemon/terminal-host.ts @@ -54,7 +54,6 @@ export class TerminalHost { private onSessionReaped: TerminalHostOptions['onSessionReaped'] private reportReadinessEvent: TerminalHostOptions['reportReadinessEvent'] private onFinalCheckpoint: TerminalHostOptions['onFinalCheckpoint'] - private maxTombstones: number private creationFenced = false private disposePromise: Promise | null = null private readonly agentSessionOwners = new ClaimedAgentPtyOwnerRegistry() @@ -71,8 +70,7 @@ export class TerminalHost { this.onSessionReaped = opts.onSessionReaped this.reportReadinessEvent = opts.reportReadinessEvent this.onFinalCheckpoint = opts.onFinalCheckpoint - this.maxTombstones = opts.maxTombstones ?? DEFAULT_MAX_TOMBSTONES - this.killedTombstones = new TerminalHostTombstones(this.maxTombstones) + this.killedTombstones = new TerminalHostTombstones(opts.maxTombstones ?? DEFAULT_MAX_TOMBSTONES) } async createOrAttach(opts: InternalCreateOrAttachOptions): Promise { @@ -123,20 +121,7 @@ export class TerminalHost { ...(this.reportReadinessEvent ? { reportReadinessEvent: this.reportReadinessEvent } : {}), - onSessionExit: (sessionId, generation) => { - const session = this.sessions.get(sessionId) - if (session) { - pruneRetiredPtyIncarnations(this.retiredIncarnations) - this.retiredIncarnations.set(sessionId, { - incarnationId: session.incarnationId, - code: session.exitCode ?? 0, - expiresAt: Date.now() + REMOTE_FOREGROUND_TOMBSTONE_RETENTION_MS - }) - } - this.agentSessionOwners.release(sessionId, generation) - this.agentSessionGenerations.forget(sessionId, generation) - this.reapSession(sessionId) - } + onSessionExit: this.handleSessionExit.bind(this) }) } }) @@ -146,6 +131,21 @@ export class TerminalHost { } } + private handleSessionExit(sessionId: string, generation: string | undefined): void { + const session = this.sessions.get(sessionId) + if (session) { + pruneRetiredPtyIncarnations(this.retiredIncarnations) + this.retiredIncarnations.set(sessionId, { + incarnationId: session.incarnationId, + code: session.exitCode ?? 0, + expiresAt: Date.now() + REMOTE_FOREGROUND_TOMBSTONE_RETENTION_MS + }) + } + this.agentSessionOwners.release(sessionId, generation) + this.agentSessionGenerations.forget(sessionId, generation) + this.reapSession(sessionId) + } + private assertCreateOrAttachAllowed(opts: InternalCreateOrAttachOptions): void { if (this.creationFenced) { throw new Error('Terminal host is shutting down') From b899b225456f0744fadb02e4129a977b1e0361bc Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:12:16 -0700 Subject: [PATCH 055/168] fix: release native PTY spawn environment after setup (#21140) Co-authored-by: m4air --- .../native-pty-spawn-env-retention/README.md | 51 ++ .../before.config.mjs | 24 + .../electron-results.json | 533 ++++++++++++++++++ .../native-pty-spawn-env-retention/fix.patch | 17 + .../node-results.json | 532 +++++++++++++++++ .../reproduce.cjs | 71 +++ .../scenario.cjs | 148 +++++ .../source-versions.json | 59 ++ .../sources.cjs | 97 ++++ .../validation.json | 53 ++ .../pty-subprocess-env-retention.test.ts | 128 +++++ .../pty-subprocess/subprocess-handle.ts | 3 +- 12 files changed, 1715 insertions(+), 1 deletion(-) create mode 100644 docs/audits/native-pty-spawn-env-retention/README.md create mode 100644 docs/audits/native-pty-spawn-env-retention/before.config.mjs create mode 100644 docs/audits/native-pty-spawn-env-retention/electron-results.json create mode 100644 docs/audits/native-pty-spawn-env-retention/fix.patch create mode 100644 docs/audits/native-pty-spawn-env-retention/node-results.json create mode 100644 docs/audits/native-pty-spawn-env-retention/reproduce.cjs create mode 100644 docs/audits/native-pty-spawn-env-retention/scenario.cjs create mode 100644 docs/audits/native-pty-spawn-env-retention/source-versions.json create mode 100644 docs/audits/native-pty-spawn-env-retention/sources.cjs create mode 100644 docs/audits/native-pty-spawn-env-retention/validation.json create mode 100644 src/main/daemon/pty-subprocess-env-retention.test.ts diff --git a/docs/audits/native-pty-spawn-env-retention/README.md b/docs/audits/native-pty-spawn-env-retention/README.md new file mode 100644 index 00000000000..35b57f8dbb1 --- /dev/null +++ b/docs/audits/native-pty-spawn-env-retention/README.md @@ -0,0 +1,51 @@ +# Native PTY spawn environment lifetime + +The native PTY handle's exit callback captured its complete creation arguments solely to read `reportsChildExitStatus`. Those arguments include the merged spawn environment. Copying that boolean before registering the callback releases the arguments and environment while the PTY remains live. + +This is per-handle retention: the original objects also collect after the handle and native event owner become unreachable. It does not establish retention after every terminal closes, native PTY memory usage, an RSS slope, or the cause of #19831. + +## Ownership and compatibility + +- `src/main/daemon/pty-subprocess.ts:72–113` creates the environment, completes preflight and native spawn, then passes a fresh object literal to `createDaemonPtySubprocessHandle`. This is the sole production call site; the caller never stores or mutates that object afterward. +- `src/main/daemon/pty-subprocess/native-pty-spawn.ts:29–74` computes `reportsChildExitStatus` synchronously from the selected native launch command. Every successful return copies the boolean into its result. It is an immutable spawn fact in this call chain. +- `src/main/daemon/pty-subprocess/subprocess-handle.ts:26–69` needs the process, projected foreground metadata, scalar exit-status fact and PATH. Its long-lived exit callback previously retained the whole argument object. The fix changes only that capture. Native spawning, native signal ownership, physical-exit ordering, disposal and output buffering are unchanged. +- The environment is a fresh merged object, but many of its string values may already be shared with `process.env`. Releasing its reachability does not imply an equivalent reduction in resident bytes. Required PATH remains reachable through `shellPathEnv`. + +`source-versions.json` records exact hashes. Main commit `291b4ddd6f1c1af480169885e0fda7f9c78ff053` exactly matches the audited wrapper baseline. Release `v1.4.198` (`e0826956fcfc532f5a1e55b5e081f2e57e553c43`) has the same environment capture and callers, but predates unrelated I/O-failure and exit-listener ordering changes. The two-line patch applies to both named sources. This is a historical source comparison, not a historical packaged-runtime reproduction. + +The change stays inside the daemon's execution-host wrapper. It adds no remote wire data or client-side process verdict, and depends on neither a git worktree nor a folder workspace. + +## Bounded before/after proof + +`sources.cjs` reverses `fix.patch` in memory and checks the exact baseline and fixed SHA-256 values before bundling either version. It imports the actual foreground tracker and pre-listener queue, and records all effective source dependency hashes and the generated bundle hash. No git refs, copied production implementation, build outputs, credentials or ignored notes are required to rerun it. + +Source and patch reads normalize CRLF to LF before reversal and hashing; recorded named-source, dependency and event-emitter hashes use canonical LF. The proof also feeds synthetic CRLF source and patch text into the loader in memory and verifies identical before/after source and hashes, without writing product files. This checks the checkout line-ending case, not a Windows runtime. + +The fixture uses the installed `node-pty` JavaScript event emitter and an inert process port. Native termination imports and `process.kill` are guarded; no native PTY, subprocess scan, OS signal, socket or window is created. WeakRefs measure one small argument object and one small environment object, with no payload amplification. The deadline is 15 seconds and the heap limit in these commands is 128 MiB. + +| Runtime | Live handle before: args / env | Live handle after: args / env | After owner drop, both versions | +| ------------------------------ | ------------------------------ | ----------------------------- | ------------------------------- | +| Node 26.6.0 | 1 / 1 | 0 / 0 | 0 / 0 | +| Electron 43.7.0 / Node 24.21.0 | 1 / 1 | 0 / 0 | 0 / 0 | + +Both versions preserve PATH, startup-delivery metadata, raw foreground lookup, pre-listener output and exit replay, normal exit codes, signal causes, unavailable wrapper status, dead-handle signal guards and idempotent disposal. `node-results.json` and `electron-results.json` contain the measured results. + +From the repository root, run the Node proof: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/native-pty-spawn-env-retention/reproduce.cjs +``` + +For Electron, run the installed Electron executable with `--expose-gc --max-old-space-size=128 docs/audits/native-pty-spawn-env-retention/reproduce.cjs`. Set `ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1` in its environment. This keeps Electron in Node mode; it creates no windows. + +## Regression checks + +The new lifetime regression measures collection before native exit, then confirms that the live handle still delivers data and exit. Two more cases preserve both exit-status interpretations after collection. Existing lifecycle, foreground identity/cadence, environment inheritance and I/O-failure cleanup suites cover neighboring contracts. + +The fixed six-file run passed 114 tests with four existing platform skips. The reversible baseline overlay ran the new and existing lifecycle suites: one expected lifetime failure, 29 passing controls. To reproduce the overlay without editing product files: + +```sh +ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/native-pty-spawn-env-retention/before.config.mjs src/main/daemon/pty-subprocess-env-retention.test.ts src/main/daemon/pty-subprocess-handle-lifecycle.test.ts +``` + +`validation.json` records the verification commands and outcomes. The pending-creation cancellation audit is separate and is not changed here. diff --git a/docs/audits/native-pty-spawn-env-retention/before.config.mjs b/docs/audits/native-pty-spawn-env-retention/before.config.mjs new file mode 100644 index 00000000000..cf47f99f81c --- /dev/null +++ b/docs/audits/native-pty-spawn-env-retention/before.config.mjs @@ -0,0 +1,24 @@ +import { resolve } from 'node:path' +import { createRequire } from 'node:module' +import { defineConfig, mergeConfig } from 'vitest/config' +import baseConfig from '../../../config/vitest.config.ts' + +const { loadSources } = createRequire(import.meta.url)( + resolve('docs/audits/native-pty-spawn-env-retention/sources.cjs') +) +const { before } = loadSources() +export default mergeConfig( + baseConfig, + defineConfig({ + plugins: [ + { + name: 'native-pty-env-before-fix', + enforce: 'pre', + transform(_code, id) { + const source = before.get(resolve(id.split('?')[0])) + return source === undefined ? undefined : { code: source, map: null } + } + } + ] + }) +) diff --git a/docs/audits/native-pty-spawn-env-retention/electron-results.json b/docs/audits/native-pty-spawn-env-retention/electron-results.json new file mode 100644 index 00000000000..207e8757ea2 --- /dev/null +++ b/docs/audits/native-pty-spawn-env-retention/electron-results.json @@ -0,0 +1,533 @@ +{ + "runtime": { + "node": "24.21.0", + "acorn": "8.18.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.2", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "148", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "0.0.0", + "simdjson": "4.6.7", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2025c", + "undici": "7.29.1", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "15.0.245.31-electron.0", + "zlib": "1.3.2.1-motley", + "zstd": "1.6.0", + "electron": "43.7.0", + "chrome": "150.0.7871.250" + }, + "sourceHashLineEndings": "canonical LF", + "crlfLoaderControl": { + "syntheticCrlfReads": 2, + "identicalSourcesAndHashes": true + }, + "scope": "Actual wrapper, foreground tracker and pre-listener queue, with the installed node-pty event emitter and an inert native process. No native PTY, process scan, OS signal or window. Small object counts, no payload amplification.", + "nodePty": { + "version": "1.1.0", + "eventEmitterSha256": "f1c14613aa90c10def4ca7238329270871997eb26f919f7074dc25533e3e75dd" + }, + "reports": { + "before": { + "whileLive": { + "args": 1, + "env": 1 + }, + "afterOwnerDrop": { + "args": 0, + "env": 0 + }, + "controls": [ + "PATH retained", + "raw foreground receiver", + "pre-listener data/exit", + "status unavailable", + "signal cause", + "dead signal guard", + "idempotent dispose" + ] + }, + "after": { + "whileLive": { + "args": 0, + "env": 0 + }, + "afterOwnerDrop": { + "args": 0, + "env": 0 + }, + "controls": [ + "PATH retained", + "raw foreground receiver", + "pre-listener data/exit", + "status unavailable", + "signal cause", + "dead signal guard", + "idempotent dispose" + ] + } + }, + "versions": { + "before": { + "sourceHashes": { + "src/main/daemon/pty-subprocess/subprocess-handle.ts": { + "before": "12ec9e8b5e5f7175310f9522c3f827b19b871c3df5b09e8b1b2c4fe1fc92ba4e", + "after": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a" + } + }, + "bundleSha256": "a546917f2c969df3c010031537338121ed0de0ce6e60f45b39ab52dc776d4a7c", + "dependencies": [ + { + "path": "src/shared/pty-slave-line-discipline-echo.ts", + "sha256": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4" + }, + { + "path": "src/main/pty/node-pty-pts-name.ts", + "sha256": "aa1a50bd935895cbd1acc03fffd16ca735ede9d563cece849e7fdede99bc95a6" + }, + { + "path": "src/main/daemon/daemon-pty-size.ts", + "sha256": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741" + }, + { + "path": "src/shared/wsl-paths.ts", + "sha256": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + }, + { + "path": "src/shared/cross-platform-path.ts", + "sha256": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3" + }, + { + "path": "src/shared/workspace-scope.ts", + "sha256": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581" + }, + { + "path": "src/shared/pty-session-id-format.ts", + "sha256": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b" + }, + { + "path": "src/shared/worktree/id.ts", + "sha256": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30" + }, + { + "path": "src/main/providers/agent-foreground-context-paths.ts", + "sha256": "df3ad7d74ff4048999231492aab490336078b915e9033cab77ab9e51b14c062f" + }, + { + "path": "src/shared/orca-cli-command-name.ts", + "sha256": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0" + }, + { + "path": "src/shared/tui-agent-config.ts", + "sha256": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad" + }, + { + "path": "src/shared/agent-node-entrypoint-identities.ts", + "sha256": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4" + }, + { + "path": "src/shared/print-mode-headless-command.ts", + "sha256": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a" + }, + { + "path": "src/shared/ante-headless-command.ts", + "sha256": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620" + }, + { + "path": "src/shared/prime-agent-headless-command.ts", + "sha256": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3" + }, + { + "path": "src/shared/agent-headless-command.ts", + "sha256": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87" + }, + { + "path": "src/shared/command-token-scanner.ts", + "sha256": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0" + }, + { + "path": "src/shared/agent-process-recognition.ts", + "sha256": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4" + }, + { + "path": "src/shared/synthetic-agent-title.ts", + "sha256": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922" + }, + { + "path": "src/shared/foreground-wrapper-agent.ts", + "sha256": "87a2caa7daa616c96cf308a5413abf3eac21fe01411ff6bcc1e166d1cd22aa77" + }, + { + "path": "src/shared/process-table-snapshot.ts", + "sha256": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9" + }, + { + "path": "src/shared/process-table-snapshot-reader.ts", + "sha256": "7d35601a2a7bf7a9c5b4a897995e0e8a15775b4d35939f3dd1abe1a042bb1c5e" + }, + { + "path": "src/shared/process-table-index.ts", + "sha256": "30f236b7d0275f05673c0a7982bb0aa67c9319762749f5a583c5c31e51e31f36" + }, + { + "path": "src/shared/shell-process-detection.ts", + "sha256": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f" + }, + { + "path": "src/main/windows/windows-command-line-recovery-health.ts", + "sha256": "a135da3611d54fecb0dba52b4d81e06ca1532680424b55e7efa315b7c886ec33" + }, + { + "path": "src/shared/child-process/windows-command-line.ts", + "sha256": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca" + }, + { + "path": "src/shared/child-process/windows-cmd-shim-resolution.ts", + "sha256": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3" + }, + { + "path": "src/shared/child-process/spawn-resolution.ts", + "sha256": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc" + }, + { + "path": "src/shared/child-process/process-tree-kill-gate.ts", + "sha256": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab" + }, + { + "path": "src/shared/child-process/process-tree-termination.ts", + "sha256": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120" + }, + { + "path": "src/shared/child-process/bounded-output-sink.ts", + "sha256": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865" + }, + { + "path": "src/shared/child-process/child-termination-reporter.ts", + "sha256": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0" + }, + { + "path": "src/shared/child-process/process-spec.ts", + "sha256": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289" + }, + { + "path": "src/shared/child-process/run-process.ts", + "sha256": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad" + }, + { + "path": "src/shared/child-process/windows-system-binary.ts", + "sha256": "0c42c7c454534a0937f4153e9708aeb243bd930d130ffc6493ac42d21405606e" + }, + { + "path": "src/main/windows/windows-process-table-cim-scan.ts", + "sha256": "ecfa487a3139cc087eae78ff61bf358f7eb62aec3b20e9d064d96b6e37862ab0" + }, + { + "path": "src/main/windows/windows-process-table.ts", + "sha256": "de0565f724f49ca393bb55c1618e697ea34e98bfee132c196d0c3e97713aabe1" + }, + { + "path": "src/main/providers/windows-foreground-process-rows.ts", + "sha256": "374accddf905f5b60fb1be4010e8160bfaf7f1a3834ecaffbf0119e555c7a765" + }, + { + "path": "src/main/providers/windows-agent-foreground-process.ts", + "sha256": "a7056960617a9a5c740209a3a96280c4e57da69b7604e17f148c0c2195d0e5e9" + }, + { + "path": "src/shared/foreground-process-selection.ts", + "sha256": "18005d15f552cb8805148cebb330636f6cec0f8d98d2eca83444aef7794d3767" + }, + { + "path": "src/main/providers/agent-foreground-process-remote-evidence.ts", + "sha256": "c46feb5f19d873bbf08ff607a6a86a53a9d982ece7aa43e471aaad640621ad4d" + }, + { + "path": "src/main/providers/agent-foreground-process-batch.ts", + "sha256": "6f8c785ddd701883f7b44231b152a0c32b8a5a3db24529347085c1ac43127ce6" + }, + { + "path": "src/main/providers/agent-foreground-process.ts", + "sha256": "1f76c512e3c308959f11665c749e9d001da03661ad5bc2230a84125bc4aa8368" + }, + { + "path": "src/main/providers/windows-pty-job-membership.ts", + "sha256": "4b5031a7cbac09c967c4269a247311e09d6b2617a630dd1743bf0a9ddac37c5f" + }, + { + "path": "src/main/daemon/pty-subprocess/pty-shell-foreground-confirmation.ts", + "sha256": "aea01ded6b790b26a06314499eaa1db993c86660a076a710a73aa06a6ba1faee" + }, + { + "path": "src/main/providers/windows-cached-agent-revalidation.ts", + "sha256": "b7e60bf9e939b069488640b291bd025386cfa413b6431edd61563dba30bcc6be" + }, + { + "path": "src/main/providers/windows-console-attached-processes.ts", + "sha256": "63de2e44605c28139204c196cc1add202c723f4896c6f36757e92ab85a7fe065" + }, + { + "path": "src/main/daemon/pty-subprocess/foreground-fallback-process.ts", + "sha256": "a2a68908fa5f59a940d8b280073f5737374cb5f6d9938c64be2a4d26433d7827" + }, + { + "path": "src/main/daemon/pty-session-id.ts", + "sha256": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878" + }, + { + "path": "src/main/daemon/pty-subprocess/foreground-process-tracker.ts", + "sha256": "c29a04d412294f2ef3b46ec2fe7fd8f680ba00aadf11db4f9faea01b1b8c8df3" + }, + { + "path": "src/shared/terminal-exit-cause.ts", + "sha256": "cdbe03be86d26c6489a257e886cb6ca6b2301dc1a82752d6a028d66de46903a2" + }, + { + "path": "src/main/daemon/pty-subprocess/pre-listener-events.ts", + "sha256": "32b290969b16c2676bbc9801cf563fc1988f16131678ce3cf0c627ffeaf401a2" + }, + { + "path": "src/main/daemon/pty-subprocess/subprocess-handle.ts", + "sha256": "12ec9e8b5e5f7175310f9522c3f827b19b871c3df5b09e8b1b2c4fe1fc92ba4e" + } + ] + }, + "after": { + "sourceHashes": { + "src/main/daemon/pty-subprocess/subprocess-handle.ts": { + "before": "12ec9e8b5e5f7175310f9522c3f827b19b871c3df5b09e8b1b2c4fe1fc92ba4e", + "after": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a" + } + }, + "bundleSha256": "54e4ef70dd3262f94b679289440b882dfedacfb5e0af325d51d1ec2a2b455929", + "dependencies": [ + { + "path": "src/shared/pty-slave-line-discipline-echo.ts", + "sha256": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4" + }, + { + "path": "src/main/pty/node-pty-pts-name.ts", + "sha256": "aa1a50bd935895cbd1acc03fffd16ca735ede9d563cece849e7fdede99bc95a6" + }, + { + "path": "src/main/daemon/daemon-pty-size.ts", + "sha256": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741" + }, + { + "path": "src/shared/wsl-paths.ts", + "sha256": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + }, + { + "path": "src/shared/cross-platform-path.ts", + "sha256": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3" + }, + { + "path": "src/shared/workspace-scope.ts", + "sha256": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581" + }, + { + "path": "src/shared/pty-session-id-format.ts", + "sha256": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b" + }, + { + "path": "src/shared/worktree/id.ts", + "sha256": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30" + }, + { + "path": "src/main/providers/agent-foreground-context-paths.ts", + "sha256": "df3ad7d74ff4048999231492aab490336078b915e9033cab77ab9e51b14c062f" + }, + { + "path": "src/shared/orca-cli-command-name.ts", + "sha256": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0" + }, + { + "path": "src/shared/tui-agent-config.ts", + "sha256": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad" + }, + { + "path": "src/shared/agent-node-entrypoint-identities.ts", + "sha256": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4" + }, + { + "path": "src/shared/print-mode-headless-command.ts", + "sha256": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a" + }, + { + "path": "src/shared/ante-headless-command.ts", + "sha256": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620" + }, + { + "path": "src/shared/prime-agent-headless-command.ts", + "sha256": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3" + }, + { + "path": "src/shared/agent-headless-command.ts", + "sha256": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87" + }, + { + "path": "src/shared/command-token-scanner.ts", + "sha256": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0" + }, + { + "path": "src/shared/agent-process-recognition.ts", + "sha256": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4" + }, + { + "path": "src/shared/synthetic-agent-title.ts", + "sha256": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922" + }, + { + "path": "src/shared/foreground-wrapper-agent.ts", + "sha256": "87a2caa7daa616c96cf308a5413abf3eac21fe01411ff6bcc1e166d1cd22aa77" + }, + { + "path": "src/shared/process-table-snapshot.ts", + "sha256": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9" + }, + { + "path": "src/shared/process-table-snapshot-reader.ts", + "sha256": "7d35601a2a7bf7a9c5b4a897995e0e8a15775b4d35939f3dd1abe1a042bb1c5e" + }, + { + "path": "src/shared/process-table-index.ts", + "sha256": "30f236b7d0275f05673c0a7982bb0aa67c9319762749f5a583c5c31e51e31f36" + }, + { + "path": "src/shared/shell-process-detection.ts", + "sha256": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f" + }, + { + "path": "src/main/windows/windows-command-line-recovery-health.ts", + "sha256": "a135da3611d54fecb0dba52b4d81e06ca1532680424b55e7efa315b7c886ec33" + }, + { + "path": "src/shared/child-process/windows-command-line.ts", + "sha256": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca" + }, + { + "path": "src/shared/child-process/windows-cmd-shim-resolution.ts", + "sha256": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3" + }, + { + "path": "src/shared/child-process/spawn-resolution.ts", + "sha256": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc" + }, + { + "path": "src/shared/child-process/process-tree-kill-gate.ts", + "sha256": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab" + }, + { + "path": "src/shared/child-process/process-tree-termination.ts", + "sha256": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120" + }, + { + "path": "src/shared/child-process/bounded-output-sink.ts", + "sha256": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865" + }, + { + "path": "src/shared/child-process/child-termination-reporter.ts", + "sha256": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0" + }, + { + "path": "src/shared/child-process/process-spec.ts", + "sha256": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289" + }, + { + "path": "src/shared/child-process/run-process.ts", + "sha256": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad" + }, + { + "path": "src/shared/child-process/windows-system-binary.ts", + "sha256": "0c42c7c454534a0937f4153e9708aeb243bd930d130ffc6493ac42d21405606e" + }, + { + "path": "src/main/windows/windows-process-table-cim-scan.ts", + "sha256": "ecfa487a3139cc087eae78ff61bf358f7eb62aec3b20e9d064d96b6e37862ab0" + }, + { + "path": "src/main/windows/windows-process-table.ts", + "sha256": "de0565f724f49ca393bb55c1618e697ea34e98bfee132c196d0c3e97713aabe1" + }, + { + "path": "src/main/providers/windows-foreground-process-rows.ts", + "sha256": "374accddf905f5b60fb1be4010e8160bfaf7f1a3834ecaffbf0119e555c7a765" + }, + { + "path": "src/main/providers/windows-agent-foreground-process.ts", + "sha256": "a7056960617a9a5c740209a3a96280c4e57da69b7604e17f148c0c2195d0e5e9" + }, + { + "path": "src/shared/foreground-process-selection.ts", + "sha256": "18005d15f552cb8805148cebb330636f6cec0f8d98d2eca83444aef7794d3767" + }, + { + "path": "src/main/providers/agent-foreground-process-remote-evidence.ts", + "sha256": "c46feb5f19d873bbf08ff607a6a86a53a9d982ece7aa43e471aaad640621ad4d" + }, + { + "path": "src/main/providers/agent-foreground-process-batch.ts", + "sha256": "6f8c785ddd701883f7b44231b152a0c32b8a5a3db24529347085c1ac43127ce6" + }, + { + "path": "src/main/providers/agent-foreground-process.ts", + "sha256": "1f76c512e3c308959f11665c749e9d001da03661ad5bc2230a84125bc4aa8368" + }, + { + "path": "src/main/providers/windows-pty-job-membership.ts", + "sha256": "4b5031a7cbac09c967c4269a247311e09d6b2617a630dd1743bf0a9ddac37c5f" + }, + { + "path": "src/main/daemon/pty-subprocess/pty-shell-foreground-confirmation.ts", + "sha256": "aea01ded6b790b26a06314499eaa1db993c86660a076a710a73aa06a6ba1faee" + }, + { + "path": "src/main/providers/windows-cached-agent-revalidation.ts", + "sha256": "b7e60bf9e939b069488640b291bd025386cfa413b6431edd61563dba30bcc6be" + }, + { + "path": "src/main/providers/windows-console-attached-processes.ts", + "sha256": "63de2e44605c28139204c196cc1add202c723f4896c6f36757e92ab85a7fe065" + }, + { + "path": "src/main/daemon/pty-subprocess/foreground-fallback-process.ts", + "sha256": "a2a68908fa5f59a940d8b280073f5737374cb5f6d9938c64be2a4d26433d7827" + }, + { + "path": "src/main/daemon/pty-session-id.ts", + "sha256": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878" + }, + { + "path": "src/main/daemon/pty-subprocess/foreground-process-tracker.ts", + "sha256": "c29a04d412294f2ef3b46ec2fe7fd8f680ba00aadf11db4f9faea01b1b8c8df3" + }, + { + "path": "src/shared/terminal-exit-cause.ts", + "sha256": "cdbe03be86d26c6489a257e886cb6ca6b2301dc1a82752d6a028d66de46903a2" + }, + { + "path": "src/main/daemon/pty-subprocess/pre-listener-events.ts", + "sha256": "32b290969b16c2676bbc9801cf563fc1988f16131678ce3cf0c627ffeaf401a2" + }, + { + "path": "src/main/daemon/pty-subprocess/subprocess-handle.ts", + "sha256": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a" + } + ] + } + } +} diff --git a/docs/audits/native-pty-spawn-env-retention/fix.patch b/docs/audits/native-pty-spawn-env-retention/fix.patch new file mode 100644 index 00000000000..f09887d66ce --- /dev/null +++ b/docs/audits/native-pty-spawn-env-retention/fix.patch @@ -0,0 +1,17 @@ +diff --git a/src/main/daemon/pty-subprocess/subprocess-handle.ts b/src/main/daemon/pty-subprocess/subprocess-handle.ts +index 974602dfe8..5d7ef16342 100644 +--- a/src/main/daemon/pty-subprocess/subprocess-handle.ts ++++ b/src/main/daemon/pty-subprocess/subprocess-handle.ts +@@ -24,4 +24,5 @@ export function createDaemonPtySubprocessHandle(args: { + startupAgentRecognition: RecognizedAgentProcess | null + }): SubprocessHandle { ++ const reportsChildExitStatus = args.reportsChildExitStatus + const proc = args.process + // node-pty exposes destroy at runtime but omits it from IPty. +@@ -57,5 +58,5 @@ export function createDaemonPtySubprocessHandle(args: { + exitCode, + signal, +- hostReportsChildExitStatus: args.reportsChildExitStatus ++ hostReportsChildExitStatus: reportsChildExitStatus + }) + }) diff --git a/docs/audits/native-pty-spawn-env-retention/node-results.json b/docs/audits/native-pty-spawn-env-retention/node-results.json new file mode 100644 index 00000000000..5c0f43bdd35 --- /dev/null +++ b/docs/audits/native-pty-spawn-env-retention/node-results.json @@ -0,0 +1,532 @@ +{ + "runtime": { + "node": "26.6.0", + "acorn": "8.17.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.3", + "libffi": "3.7.1", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "147", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "3.6.3", + "simdjson": "4.6.6", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2026a", + "undici": "8.9.0", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "14.6.202.34-node.26", + "zlib": "1.2.12", + "zstd": "1.5.7" + }, + "sourceHashLineEndings": "canonical LF", + "crlfLoaderControl": { + "syntheticCrlfReads": 2, + "identicalSourcesAndHashes": true + }, + "scope": "Actual wrapper, foreground tracker and pre-listener queue, with the installed node-pty event emitter and an inert native process. No native PTY, process scan, OS signal or window. Small object counts, no payload amplification.", + "nodePty": { + "version": "1.1.0", + "eventEmitterSha256": "f1c14613aa90c10def4ca7238329270871997eb26f919f7074dc25533e3e75dd" + }, + "reports": { + "before": { + "whileLive": { + "args": 1, + "env": 1 + }, + "afterOwnerDrop": { + "args": 0, + "env": 0 + }, + "controls": [ + "PATH retained", + "raw foreground receiver", + "pre-listener data/exit", + "status unavailable", + "signal cause", + "dead signal guard", + "idempotent dispose" + ] + }, + "after": { + "whileLive": { + "args": 0, + "env": 0 + }, + "afterOwnerDrop": { + "args": 0, + "env": 0 + }, + "controls": [ + "PATH retained", + "raw foreground receiver", + "pre-listener data/exit", + "status unavailable", + "signal cause", + "dead signal guard", + "idempotent dispose" + ] + } + }, + "versions": { + "before": { + "sourceHashes": { + "src/main/daemon/pty-subprocess/subprocess-handle.ts": { + "before": "12ec9e8b5e5f7175310f9522c3f827b19b871c3df5b09e8b1b2c4fe1fc92ba4e", + "after": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a" + } + }, + "bundleSha256": "a546917f2c969df3c010031537338121ed0de0ce6e60f45b39ab52dc776d4a7c", + "dependencies": [ + { + "path": "src/shared/pty-slave-line-discipline-echo.ts", + "sha256": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4" + }, + { + "path": "src/main/pty/node-pty-pts-name.ts", + "sha256": "aa1a50bd935895cbd1acc03fffd16ca735ede9d563cece849e7fdede99bc95a6" + }, + { + "path": "src/main/daemon/daemon-pty-size.ts", + "sha256": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741" + }, + { + "path": "src/shared/wsl-paths.ts", + "sha256": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + }, + { + "path": "src/shared/cross-platform-path.ts", + "sha256": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3" + }, + { + "path": "src/shared/workspace-scope.ts", + "sha256": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581" + }, + { + "path": "src/shared/pty-session-id-format.ts", + "sha256": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b" + }, + { + "path": "src/shared/worktree/id.ts", + "sha256": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30" + }, + { + "path": "src/main/providers/agent-foreground-context-paths.ts", + "sha256": "df3ad7d74ff4048999231492aab490336078b915e9033cab77ab9e51b14c062f" + }, + { + "path": "src/shared/orca-cli-command-name.ts", + "sha256": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0" + }, + { + "path": "src/shared/tui-agent-config.ts", + "sha256": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad" + }, + { + "path": "src/shared/agent-node-entrypoint-identities.ts", + "sha256": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4" + }, + { + "path": "src/shared/print-mode-headless-command.ts", + "sha256": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a" + }, + { + "path": "src/shared/ante-headless-command.ts", + "sha256": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620" + }, + { + "path": "src/shared/prime-agent-headless-command.ts", + "sha256": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3" + }, + { + "path": "src/shared/agent-headless-command.ts", + "sha256": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87" + }, + { + "path": "src/shared/command-token-scanner.ts", + "sha256": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0" + }, + { + "path": "src/shared/agent-process-recognition.ts", + "sha256": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4" + }, + { + "path": "src/shared/synthetic-agent-title.ts", + "sha256": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922" + }, + { + "path": "src/shared/foreground-wrapper-agent.ts", + "sha256": "87a2caa7daa616c96cf308a5413abf3eac21fe01411ff6bcc1e166d1cd22aa77" + }, + { + "path": "src/shared/process-table-snapshot.ts", + "sha256": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9" + }, + { + "path": "src/shared/process-table-snapshot-reader.ts", + "sha256": "7d35601a2a7bf7a9c5b4a897995e0e8a15775b4d35939f3dd1abe1a042bb1c5e" + }, + { + "path": "src/shared/process-table-index.ts", + "sha256": "30f236b7d0275f05673c0a7982bb0aa67c9319762749f5a583c5c31e51e31f36" + }, + { + "path": "src/shared/shell-process-detection.ts", + "sha256": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f" + }, + { + "path": "src/main/windows/windows-command-line-recovery-health.ts", + "sha256": "a135da3611d54fecb0dba52b4d81e06ca1532680424b55e7efa315b7c886ec33" + }, + { + "path": "src/shared/child-process/windows-command-line.ts", + "sha256": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca" + }, + { + "path": "src/shared/child-process/windows-cmd-shim-resolution.ts", + "sha256": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3" + }, + { + "path": "src/shared/child-process/spawn-resolution.ts", + "sha256": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc" + }, + { + "path": "src/shared/child-process/process-tree-kill-gate.ts", + "sha256": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab" + }, + { + "path": "src/shared/child-process/process-tree-termination.ts", + "sha256": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120" + }, + { + "path": "src/shared/child-process/bounded-output-sink.ts", + "sha256": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865" + }, + { + "path": "src/shared/child-process/child-termination-reporter.ts", + "sha256": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0" + }, + { + "path": "src/shared/child-process/process-spec.ts", + "sha256": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289" + }, + { + "path": "src/shared/child-process/run-process.ts", + "sha256": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad" + }, + { + "path": "src/shared/child-process/windows-system-binary.ts", + "sha256": "0c42c7c454534a0937f4153e9708aeb243bd930d130ffc6493ac42d21405606e" + }, + { + "path": "src/main/windows/windows-process-table-cim-scan.ts", + "sha256": "ecfa487a3139cc087eae78ff61bf358f7eb62aec3b20e9d064d96b6e37862ab0" + }, + { + "path": "src/main/windows/windows-process-table.ts", + "sha256": "de0565f724f49ca393bb55c1618e697ea34e98bfee132c196d0c3e97713aabe1" + }, + { + "path": "src/main/providers/windows-foreground-process-rows.ts", + "sha256": "374accddf905f5b60fb1be4010e8160bfaf7f1a3834ecaffbf0119e555c7a765" + }, + { + "path": "src/main/providers/windows-agent-foreground-process.ts", + "sha256": "a7056960617a9a5c740209a3a96280c4e57da69b7604e17f148c0c2195d0e5e9" + }, + { + "path": "src/shared/foreground-process-selection.ts", + "sha256": "18005d15f552cb8805148cebb330636f6cec0f8d98d2eca83444aef7794d3767" + }, + { + "path": "src/main/providers/agent-foreground-process-remote-evidence.ts", + "sha256": "c46feb5f19d873bbf08ff607a6a86a53a9d982ece7aa43e471aaad640621ad4d" + }, + { + "path": "src/main/providers/agent-foreground-process-batch.ts", + "sha256": "6f8c785ddd701883f7b44231b152a0c32b8a5a3db24529347085c1ac43127ce6" + }, + { + "path": "src/main/providers/agent-foreground-process.ts", + "sha256": "1f76c512e3c308959f11665c749e9d001da03661ad5bc2230a84125bc4aa8368" + }, + { + "path": "src/main/providers/windows-pty-job-membership.ts", + "sha256": "4b5031a7cbac09c967c4269a247311e09d6b2617a630dd1743bf0a9ddac37c5f" + }, + { + "path": "src/main/daemon/pty-subprocess/pty-shell-foreground-confirmation.ts", + "sha256": "aea01ded6b790b26a06314499eaa1db993c86660a076a710a73aa06a6ba1faee" + }, + { + "path": "src/main/providers/windows-cached-agent-revalidation.ts", + "sha256": "b7e60bf9e939b069488640b291bd025386cfa413b6431edd61563dba30bcc6be" + }, + { + "path": "src/main/providers/windows-console-attached-processes.ts", + "sha256": "63de2e44605c28139204c196cc1add202c723f4896c6f36757e92ab85a7fe065" + }, + { + "path": "src/main/daemon/pty-subprocess/foreground-fallback-process.ts", + "sha256": "a2a68908fa5f59a940d8b280073f5737374cb5f6d9938c64be2a4d26433d7827" + }, + { + "path": "src/main/daemon/pty-session-id.ts", + "sha256": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878" + }, + { + "path": "src/main/daemon/pty-subprocess/foreground-process-tracker.ts", + "sha256": "c29a04d412294f2ef3b46ec2fe7fd8f680ba00aadf11db4f9faea01b1b8c8df3" + }, + { + "path": "src/shared/terminal-exit-cause.ts", + "sha256": "cdbe03be86d26c6489a257e886cb6ca6b2301dc1a82752d6a028d66de46903a2" + }, + { + "path": "src/main/daemon/pty-subprocess/pre-listener-events.ts", + "sha256": "32b290969b16c2676bbc9801cf563fc1988f16131678ce3cf0c627ffeaf401a2" + }, + { + "path": "src/main/daemon/pty-subprocess/subprocess-handle.ts", + "sha256": "12ec9e8b5e5f7175310f9522c3f827b19b871c3df5b09e8b1b2c4fe1fc92ba4e" + } + ] + }, + "after": { + "sourceHashes": { + "src/main/daemon/pty-subprocess/subprocess-handle.ts": { + "before": "12ec9e8b5e5f7175310f9522c3f827b19b871c3df5b09e8b1b2c4fe1fc92ba4e", + "after": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a" + } + }, + "bundleSha256": "54e4ef70dd3262f94b679289440b882dfedacfb5e0af325d51d1ec2a2b455929", + "dependencies": [ + { + "path": "src/shared/pty-slave-line-discipline-echo.ts", + "sha256": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4" + }, + { + "path": "src/main/pty/node-pty-pts-name.ts", + "sha256": "aa1a50bd935895cbd1acc03fffd16ca735ede9d563cece849e7fdede99bc95a6" + }, + { + "path": "src/main/daemon/daemon-pty-size.ts", + "sha256": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741" + }, + { + "path": "src/shared/wsl-paths.ts", + "sha256": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + }, + { + "path": "src/shared/cross-platform-path.ts", + "sha256": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3" + }, + { + "path": "src/shared/workspace-scope.ts", + "sha256": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581" + }, + { + "path": "src/shared/pty-session-id-format.ts", + "sha256": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b" + }, + { + "path": "src/shared/worktree/id.ts", + "sha256": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30" + }, + { + "path": "src/main/providers/agent-foreground-context-paths.ts", + "sha256": "df3ad7d74ff4048999231492aab490336078b915e9033cab77ab9e51b14c062f" + }, + { + "path": "src/shared/orca-cli-command-name.ts", + "sha256": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0" + }, + { + "path": "src/shared/tui-agent-config.ts", + "sha256": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad" + }, + { + "path": "src/shared/agent-node-entrypoint-identities.ts", + "sha256": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4" + }, + { + "path": "src/shared/print-mode-headless-command.ts", + "sha256": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a" + }, + { + "path": "src/shared/ante-headless-command.ts", + "sha256": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620" + }, + { + "path": "src/shared/prime-agent-headless-command.ts", + "sha256": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3" + }, + { + "path": "src/shared/agent-headless-command.ts", + "sha256": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87" + }, + { + "path": "src/shared/command-token-scanner.ts", + "sha256": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0" + }, + { + "path": "src/shared/agent-process-recognition.ts", + "sha256": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4" + }, + { + "path": "src/shared/synthetic-agent-title.ts", + "sha256": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922" + }, + { + "path": "src/shared/foreground-wrapper-agent.ts", + "sha256": "87a2caa7daa616c96cf308a5413abf3eac21fe01411ff6bcc1e166d1cd22aa77" + }, + { + "path": "src/shared/process-table-snapshot.ts", + "sha256": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9" + }, + { + "path": "src/shared/process-table-snapshot-reader.ts", + "sha256": "7d35601a2a7bf7a9c5b4a897995e0e8a15775b4d35939f3dd1abe1a042bb1c5e" + }, + { + "path": "src/shared/process-table-index.ts", + "sha256": "30f236b7d0275f05673c0a7982bb0aa67c9319762749f5a583c5c31e51e31f36" + }, + { + "path": "src/shared/shell-process-detection.ts", + "sha256": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f" + }, + { + "path": "src/main/windows/windows-command-line-recovery-health.ts", + "sha256": "a135da3611d54fecb0dba52b4d81e06ca1532680424b55e7efa315b7c886ec33" + }, + { + "path": "src/shared/child-process/windows-command-line.ts", + "sha256": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca" + }, + { + "path": "src/shared/child-process/windows-cmd-shim-resolution.ts", + "sha256": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3" + }, + { + "path": "src/shared/child-process/spawn-resolution.ts", + "sha256": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc" + }, + { + "path": "src/shared/child-process/process-tree-kill-gate.ts", + "sha256": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab" + }, + { + "path": "src/shared/child-process/process-tree-termination.ts", + "sha256": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120" + }, + { + "path": "src/shared/child-process/bounded-output-sink.ts", + "sha256": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865" + }, + { + "path": "src/shared/child-process/child-termination-reporter.ts", + "sha256": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0" + }, + { + "path": "src/shared/child-process/process-spec.ts", + "sha256": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289" + }, + { + "path": "src/shared/child-process/run-process.ts", + "sha256": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad" + }, + { + "path": "src/shared/child-process/windows-system-binary.ts", + "sha256": "0c42c7c454534a0937f4153e9708aeb243bd930d130ffc6493ac42d21405606e" + }, + { + "path": "src/main/windows/windows-process-table-cim-scan.ts", + "sha256": "ecfa487a3139cc087eae78ff61bf358f7eb62aec3b20e9d064d96b6e37862ab0" + }, + { + "path": "src/main/windows/windows-process-table.ts", + "sha256": "de0565f724f49ca393bb55c1618e697ea34e98bfee132c196d0c3e97713aabe1" + }, + { + "path": "src/main/providers/windows-foreground-process-rows.ts", + "sha256": "374accddf905f5b60fb1be4010e8160bfaf7f1a3834ecaffbf0119e555c7a765" + }, + { + "path": "src/main/providers/windows-agent-foreground-process.ts", + "sha256": "a7056960617a9a5c740209a3a96280c4e57da69b7604e17f148c0c2195d0e5e9" + }, + { + "path": "src/shared/foreground-process-selection.ts", + "sha256": "18005d15f552cb8805148cebb330636f6cec0f8d98d2eca83444aef7794d3767" + }, + { + "path": "src/main/providers/agent-foreground-process-remote-evidence.ts", + "sha256": "c46feb5f19d873bbf08ff607a6a86a53a9d982ece7aa43e471aaad640621ad4d" + }, + { + "path": "src/main/providers/agent-foreground-process-batch.ts", + "sha256": "6f8c785ddd701883f7b44231b152a0c32b8a5a3db24529347085c1ac43127ce6" + }, + { + "path": "src/main/providers/agent-foreground-process.ts", + "sha256": "1f76c512e3c308959f11665c749e9d001da03661ad5bc2230a84125bc4aa8368" + }, + { + "path": "src/main/providers/windows-pty-job-membership.ts", + "sha256": "4b5031a7cbac09c967c4269a247311e09d6b2617a630dd1743bf0a9ddac37c5f" + }, + { + "path": "src/main/daemon/pty-subprocess/pty-shell-foreground-confirmation.ts", + "sha256": "aea01ded6b790b26a06314499eaa1db993c86660a076a710a73aa06a6ba1faee" + }, + { + "path": "src/main/providers/windows-cached-agent-revalidation.ts", + "sha256": "b7e60bf9e939b069488640b291bd025386cfa413b6431edd61563dba30bcc6be" + }, + { + "path": "src/main/providers/windows-console-attached-processes.ts", + "sha256": "63de2e44605c28139204c196cc1add202c723f4896c6f36757e92ab85a7fe065" + }, + { + "path": "src/main/daemon/pty-subprocess/foreground-fallback-process.ts", + "sha256": "a2a68908fa5f59a940d8b280073f5737374cb5f6d9938c64be2a4d26433d7827" + }, + { + "path": "src/main/daemon/pty-session-id.ts", + "sha256": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878" + }, + { + "path": "src/main/daemon/pty-subprocess/foreground-process-tracker.ts", + "sha256": "c29a04d412294f2ef3b46ec2fe7fd8f680ba00aadf11db4f9faea01b1b8c8df3" + }, + { + "path": "src/shared/terminal-exit-cause.ts", + "sha256": "cdbe03be86d26c6489a257e886cb6ca6b2301dc1a82752d6a028d66de46903a2" + }, + { + "path": "src/main/daemon/pty-subprocess/pre-listener-events.ts", + "sha256": "32b290969b16c2676bbc9801cf563fc1988f16131678ce3cf0c627ffeaf401a2" + }, + { + "path": "src/main/daemon/pty-subprocess/subprocess-handle.ts", + "sha256": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a" + } + ] + } + } +} diff --git a/docs/audits/native-pty-spawn-env-retention/reproduce.cjs b/docs/audits/native-pty-spawn-env-retention/reproduce.cjs new file mode 100644 index 00000000000..693f4ceb234 --- /dev/null +++ b/docs/audits/native-pty-spawn-env-retention/reproduce.cjs @@ -0,0 +1,71 @@ +const assert = require('node:assert/strict') +const { createHash } = require('node:crypto') +const { readFileSync, writeFileSync } = require('node:fs') +const path = require('node:path') +const { canonicalLf, load, loadSources } = require('./sources.cjs') +const run = require('./scenario.cjs') + +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function') + +function checkCrlfLoader() { + const baseline = loadSources() + let syntheticCrlfReads = 0 + const crlf = loadSources({ + readText(file) { + syntheticCrlfReads++ + return canonicalLf(readFileSync(file, 'utf8')).replace(/\n/g, '\r\n') + } + }) + assert.equal(syntheticCrlfReads, 2) + assert.deepEqual(crlf.before, baseline.before) + assert.deepEqual(crlf.after, baseline.after) + assert.deepEqual(crlf.hashes, baseline.hashes) + return { syntheticCrlfReads, identicalSourcesAndHashes: true } +} + +async function main() { + const timer = setTimeout(() => { + process.stderr.write('proof deadline\n') + process.exit(2) + }, 15_000) + const crlfLoaderControl = checkCrlfLoader() + const reports = {} + const versions = {} + for (const [mode, fixed] of [ + ['before', false], + ['after', true] + ]) { + const loaded = await load(fixed) + reports[mode] = await run(loaded.create, fixed) + versions[mode] = { + sourceHashes: loaded.hashes, + bundleSha256: loaded.bundleSha256, + dependencies: loaded.dependencies + } + } + clearTimeout(timer) + const emitter = require.resolve('node-pty/lib/eventEmitter2') + const report = { + runtime: process.versions, + sourceHashLineEndings: 'canonical LF', + crlfLoaderControl, + scope: + 'Actual wrapper, foreground tracker and pre-listener queue, with the installed node-pty event emitter and an inert native process. No native PTY, process scan, OS signal or window. Small object counts, no payload amplification.', + nodePty: { + version: require('node-pty/package.json').version, + eventEmitterSha256: createHash('sha256') + .update(canonicalLf(readFileSync(emitter, 'utf8'))) + .digest('hex') + }, + reports, + versions + } + const name = process.versions.electron ? 'electron-results.json' : 'node-results.json' + writeFileSync(path.join(__dirname, name), `${JSON.stringify(report, null, 2)}\n`) + process.stdout.write(`${JSON.stringify({ runtime: process.versions.node, reports }, null, 2)}\n`) +} +main().catch((error) => { + process.stderr.write(`${error.stack}\n`) + process.exit(1) +}) diff --git a/docs/audits/native-pty-spawn-env-retention/scenario.cjs b/docs/audits/native-pty-spawn-env-retention/scenario.cjs new file mode 100644 index 00000000000..7726626cf93 --- /dev/null +++ b/docs/audits/native-pty-spawn-env-retention/scenario.cjs @@ -0,0 +1,148 @@ +const assert = require('node:assert/strict') +const { EventEmitter2 } = require('node-pty/lib/eventEmitter2') + +function nativePort() { + const data = new EventEmitter2() + const exit = new EventEmitter2() + const calls = { writes: 0, resizes: 0, pauses: 0, resumes: 0, kills: 0, destroys: 0 } + return { + process: { + pid: 0, + process: 'audit-shell', + onData: data.event, + onExit: exit.event, + write() { + calls.writes++ + }, + resize() { + calls.resizes++ + }, + pause() { + calls.pauses++ + }, + resume() { + calls.resumes++ + }, + clear() {}, + kill() { + calls.kills++ + }, + destroy() { + calls.destroys++ + } + }, + emitData: (value) => data.fire(value), + emitExit: (value) => exit.fire(value), + calls + } +} + +function start(create, reportsChildExitStatus) { + const native = nativePort() + const env = { PATH: '/synthetic/audit/bin', RETENTION_FIXTURE: 'small ordinary field' } + const args = { + process: native.process, + shellPath: '/synthetic/audit-shell', + spawnCwd: '/synthetic', + requestedCwd: '/synthetic', + sessionId: 'native-env-audit', + startupAgentRecognition: null, + env, + startupCommandDeliveredInShellArgs: true, + reportsChildExitStatus + } + return { + handle: create(args), + native, + refs: { args: new WeakRef(args), env: new WeakRef(env) } + } +} + +async function collect() { + for (let round = 0; round < 6; round++) { + await new Promise(setImmediate) + global.gc() + } +} +function counts(refs) { + return Object.fromEntries( + ['args', 'env'].map((key) => [key, refs.filter((ref) => ref[key].deref()).length]) + ) +} + +async function run(create, fixed) { + const originalKill = process.kill + const nativeSignals = [] + process.kill = (...args) => { + nativeSignals.push(args) + throw new Error('No OS signal permitted in proof') + } + try { + let owner = start(create, true) + const refs = [owner.refs] + await collect() + const whileLive = counts(refs) + assert.deepEqual(whileLive, { args: fixed ? 0 : 1, env: fixed ? 0 : 1 }) + assert.equal(owner.handle.shellPathEnv, '/synthetic/audit/bin') + assert.equal(owner.handle.startupCommandDeliveredInShellArgs, true) + assert.equal(owner.handle.getForegroundProcess({ rawFallback: true }), 'audit-shell') + const output = [] + const exits = [] + owner.native.emitData('early-output') + owner.handle.onData((data) => output.push(data)) + owner.handle.onExit((code, cause) => exits.push({ code, cause })) + owner.handle.write('a') + owner.handle.resize(80, 24) + owner.handle.pause() + owner.handle.resume() + owner.native.emitExit({ exitCode: 7, signal: 0 }) + assert.deepEqual(exits, [{ code: 7, cause: { kind: 'exited', exitCode: 7 } }]) + assert.deepEqual(output, ['early-output']) + owner.handle.write('after-exit') + owner.handle.kill() + owner.handle.forceKill() + owner.handle.signal('SIGKILL') + assert.equal(owner.native.calls.writes, 1) + assert.equal(owner.native.calls.kills, 0) + owner.handle.dispose() + owner.handle.dispose() + assert.equal(owner.native.calls.destroys, 1) + owner = null + await collect() + const afterOwnerDrop = counts(refs) + assert.deepEqual(afterOwnerDrop, { args: 0, env: 0 }) + + const unavailable = start(create, false) + const unavailableExits = [] + unavailable.native.emitExit({ exitCode: 0, signal: 9 }) + unavailable.handle.onExit((code, cause) => unavailableExits.push({ code, cause })) + assert.deepEqual(unavailableExits, [ + { code: 0, cause: { kind: 'unknown', reason: 'host_status_unavailable' } } + ]) + unavailable.handle.dispose() + const signaled = start(create, true) + const signaledExits = [] + signaled.handle.onExit((code, cause) => signaledExits.push({ code, cause })) + signaled.native.emitExit({ exitCode: 0, signal: 9 }) + assert.deepEqual(signaledExits, [{ code: 0, cause: { kind: 'signaled', signal: 9 } }]) + signaled.handle.dispose() + assert.deepEqual(nativeSignals, []) + return { + whileLive, + afterOwnerDrop, + controls: [ + 'PATH retained', + 'raw foreground receiver', + 'pre-listener data/exit', + 'status unavailable', + 'signal cause', + 'dead signal guard', + 'idempotent dispose' + ] + } + } finally { + process.kill = originalKill + } +} + +module.exports = run diff --git a/docs/audits/native-pty-spawn-env-retention/source-versions.json b/docs/audits/native-pty-spawn-env-retention/source-versions.json new file mode 100644 index 00000000000..61a21fc8792 --- /dev/null +++ b/docs/audits/native-pty-spawn-env-retention/source-versions.json @@ -0,0 +1,59 @@ +{ + "baselineHashes": { + "src/main/daemon/pty-subprocess/subprocess-handle.ts": "12ec9e8b5e5f7175310f9522c3f827b19b871c3df5b09e8b1b2c4fe1fc92ba4e" + }, + "fixedHashes": { + "src/main/daemon/pty-subprocess/subprocess-handle.ts": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a" + }, + "namedRefs": [ + { + "ref": "HEAD", + "revision": "b5fc27171c0273e0b9e2af873259e85724aea8c3", + "beforeSha256": "12ec9e8b5e5f7175310f9522c3f827b19b871c3df5b09e8b1b2c4fe1fc92ba4e", + "projectedSha256": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a", + "matchesAuditBaseline": true, + "sameExitCallbackCapture": true, + "patchApplies": true + }, + { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "revision": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "beforeSha256": "12ec9e8b5e5f7175310f9522c3f827b19b871c3df5b09e8b1b2c4fe1fc92ba4e", + "projectedSha256": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a", + "matchesAuditBaseline": true, + "sameExitCallbackCapture": true, + "patchApplies": true + }, + { + "ref": "v1.4.198", + "revision": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "beforeSha256": "4623b60a0e362bb3cf218787573966aa056ee5fd1bdefc39fb5293446e4af70b", + "projectedSha256": "ed5665c33d0d4157836a5b97bd934c7c2894d84b9d8a905544fb19a7863dc262", + "matchesAuditBaseline": false, + "sameExitCallbackCapture": true, + "patchApplies": true + } + ], + "historicalRuntimeReproduced": false, + "callerProvenance": [ + { + "path": "src/main/daemon/pty-subprocess.ts", + "currentSha256": "9f32c99c79fd3a23fd61d572146f81c3680fb5cbc13337e4fb3179856a0d7dc1", + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": "9f32c99c79fd3a23fd61d572146f81c3680fb5cbc13337e4fb3179856a0d7dc1", + "v1.4.198": "9f32c99c79fd3a23fd61d572146f81c3680fb5cbc13337e4fb3179856a0d7dc1" + }, + { + "path": "src/main/daemon/pty-subprocess/native-pty-spawn.ts", + "currentSha256": "3752b779e06d7c004bb6acfba3188f1e5dfb589bc6a9bbe024f8b5e80dad68a5", + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": "3752b779e06d7c004bb6acfba3188f1e5dfb589bc6a9bbe024f8b5e80dad68a5", + "v1.4.198": "3752b779e06d7c004bb6acfba3188f1e5dfb589bc6a9bbe024f8b5e80dad68a5" + }, + { + "path": "src/main/daemon/pty-subprocess/spawn-environment.ts", + "currentSha256": "ceb93773a9c57f4b0ae246399600f648c298187b158b3cc5f0183ffb85d2a565", + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": "ceb93773a9c57f4b0ae246399600f648c298187b158b3cc5f0183ffb85d2a565", + "v1.4.198": "ceb93773a9c57f4b0ae246399600f648c298187b158b3cc5f0183ffb85d2a565" + } + ], + "sourceHashLineEndings": "canonical LF" +} diff --git a/docs/audits/native-pty-spawn-env-retention/sources.cjs b/docs/audits/native-pty-spawn-env-retention/sources.cjs new file mode 100644 index 00000000000..a4b54e7b028 --- /dev/null +++ b/docs/audits/native-pty-spawn-env-retention/sources.cjs @@ -0,0 +1,97 @@ +const assert = require('node:assert/strict') +const { createHash } = require('node:crypto') +const { readFileSync } = require('node:fs') +const path = require('node:path') +const { resolve } = path +const Module = require('node:module') +const esbuild = require('esbuild') +const sha = (value) => createHash('sha256').update(value).digest('hex') +const { applyPatch, parsePatch, reversePatch } = require('diff') + +const canonicalLf = (text) => text.replace(/\r\n/g, '\n') + +function loadSources({ readText = (file) => readFileSync(file, 'utf8') } = {}) { + const root = resolve(__dirname, '../../..') + const expected = JSON.parse(readFileSync(resolve(__dirname, 'source-versions.json'), 'utf8')) + const parsed = parsePatch(canonicalLf(readText(resolve(__dirname, 'fix.patch')))) + const before = new Map() + const after = new Map() + const hashes = {} + assert.equal(parsed.length, 1) + for (const patch of parsed) { + const path = patch.newFileName.replace(/^b\//, '') + assert.ok(Object.hasOwn(expected.baselineHashes, path), `Unexpected patch path: ${path}`) + const absolute = resolve(root, path) + const current = canonicalLf(readText(absolute)) + const baseline = applyPatch(current, reversePatch(patch)) + assert.notEqual(baseline, false, `Source changed; review fix.patch: ${path}`) + const hash = (source) => createHash('sha256').update(source).digest('hex') + assert.equal(hash(baseline), expected.baselineHashes[path], `Baseline drift: ${path}`) + assert.equal(hash(current), expected.fixedHashes[path], `Fixed source drift: ${path}`) + before.set(absolute, baseline) + after.set(absolute, current) + hashes[path] = { before: hash(baseline), after: hash(current) } + } + return { root, before, after, hashes } +} + +async function load(fixed) { + const { root, before, after, hashes } = loadSources() + const sourcePath = 'src/main/daemon/pty-subprocess/subprocess-handle.ts' + const selected = (fixed ? after : before).get(path.join(root, sourcePath)) + const build = await esbuild.build({ + entryPoints: [path.join(root, sourcePath)], + bundle: true, + platform: 'node', + format: 'cjs', + packages: 'external', + write: false, + metafile: true, + plugins: [ + { + name: 'capture-only-projection', + setup(build) { + build.onLoad({ filter: /subprocess-handle\.ts$/ }, (args) => { + assert.equal(args.path, path.join(root, sourcePath)) + return { contents: selected, loader: 'ts' } + }) + build.onResolve( + { filter: /(?:posix-pty-process-groups|posix-pty-foreground-group|windows-pty-job)$/ }, + (args) => ({ path: args.path, namespace: 'guard' }) + ) + build.onLoad({ filter: /.*/, namespace: 'guard' }, () => ({ + contents: ` + const unexpected = () => { throw new Error('No native termination permitted in proof') } + export const forceKillPosixPtyProcessGroups = unexpected + export const signalPosixPtyForegroundGroup = unexpected + export const terminatePtyJob = unexpected + export const isPtyJobOwnershipAvailable = unexpected + export const listPtyJobProcessIds = unexpected + `, + loader: 'js' + })) + } + } + ] + }) + const file = path.join(root, 'native-pty-env-proof.cjs') + const module_ = new Module(file, module) + module_.filename = file + module_.paths = Module._nodeModulePaths(root) + module_._compile(build.outputFiles[0].text, file) + return { + create: module_.exports.createDaemonPtySubprocessHandle, + hashes, + bundleSha256: sha(build.outputFiles[0].contents), + dependencies: Object.keys(build.metafile.inputs) + .filter((file) => file.startsWith('src/')) + .map((file) => ({ + path: file, + sha256: sha( + file === sourcePath ? selected : canonicalLf(readFileSync(path.join(root, file), 'utf8')) + ) + })) + } +} + +module.exports = { canonicalLf, load, loadSources } diff --git a/docs/audits/native-pty-spawn-env-retention/validation.json b/docs/audits/native-pty-spawn-env-retention/validation.json new file mode 100644 index 00000000000..46e4dbb7ee2 --- /dev/null +++ b/docs/audits/native-pty-spawn-env-retention/validation.json @@ -0,0 +1,53 @@ +{ + "tests": { + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/daemon/pty-subprocess-env-retention.test.ts src/main/daemon/pty-subprocess-handle-lifecycle.test.ts src/main/daemon/pty-subprocess-foreground-identity.test.ts src/main/daemon/pty-subprocess-foreground-scan-cadence.test.ts src/main/daemon/pty-subprocess-env-inheritance.test.ts src/main/daemon/pty-subprocess-io-failure-cleanup.test.ts", + "passed": 114, + "skipped": 4, + "files": 6, + "exitCode": 0 + }, + "baselineOverlay": { + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/native-pty-spawn-env-retention/before.config.mjs src/main/daemon/pty-subprocess-env-retention.test.ts src/main/daemon/pty-subprocess-handle-lifecycle.test.ts", + "passed": 29, + "expectedFailed": 1, + "failure": "releases completed spawn arguments while a live handle still forwards data and exit", + "exitCode": 1 + }, + "typecheck": { + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm tc:node", + "exitCode": 0 + }, + "lint": { + "files": [ + "src/main/daemon/pty-subprocess/subprocess-handle.ts", + "src/main/daemon/pty-subprocess-env-retention.test.ts", + "docs/audits/native-pty-spawn-env-retention/sources.cjs", + "docs/audits/native-pty-spawn-env-retention/scenario.cjs", + "docs/audits/native-pty-spawn-env-retention/reproduce.cjs", + "docs/audits/native-pty-spawn-env-retention/before.config.mjs" + ], + "ordinary": "pnpm exec oxlint --no-ignore ", + "typeAware": "pnpm exec oxlint --no-ignore --type-aware --config config/oxlint-code-quality-type-aware.json --deny-warnings", + "exitCodes": [0, 0] + }, + "changedQuality": { + "command": "ORCA_BACKGROUND_LAUNCH=1 ORCA_CODE_QUALITY_BASE=b5fc27171c0273e0b9e2af873259e85724aea8c3 pnpm run check:code-quality:changed", + "exitCode": 0, + "changedFiles": 2, + "newFindings": 0 + }, + "proofs": { + "node": "ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=128 docs/audits/native-pty-spawn-env-retention/reproduce.cjs", + "electron": "Installed Electron executable with ELECTRON_RUN_AS_NODE=1, ORCA_BACKGROUND_LAUNCH=1, --expose-gc --max-old-space-size=128 and the same proof path.", + "exitCodes": [0, 0] + }, + "format": "Each source and artifact TS/CJS/MJS/MD/JSON file checked using oxfmt --stdin-filepath; patch excluded.", + "gitDiffCheckExitCode": 0, + "crlfLoaderControl": { + "runtimes": ["Node 26.6.0", "Electron 43.7.0 / Node 24.21.0"], + "syntheticCrlfReads": 2, + "identicalBeforeAfterSourcesAndHashes": true, + "productWrites": false, + "namedSourceHashesVerifiedAsCanonicalLf": true + } +} diff --git a/src/main/daemon/pty-subprocess-env-retention.test.ts b/src/main/daemon/pty-subprocess-env-retention.test.ts new file mode 100644 index 00000000000..c936fe28d30 --- /dev/null +++ b/src/main/daemon/pty-subprocess-env-retention.test.ts @@ -0,0 +1,128 @@ +import { EventEmitter } from 'node:events' +import { describe, expect, it, vi } from 'vitest' +import type * as pty from 'node-pty' +import { createDaemonPtySubprocessHandle } from './pty-subprocess/subprocess-handle' + +vi.mock('../pty/posix-pty-process-groups', () => ({ + forceKillPosixPtyProcessGroups: () => { + throw new Error('Unexpected native termination') + } +})) +vi.mock('../pty/posix-pty-foreground-group', () => ({ + signalPosixPtyForegroundGroup: () => { + throw new Error('Unexpected native signal') + } +})) + +function nativePort() { + const data = new EventEmitter() + const exit = new EventEmitter() + const process_ = { + pid: 0, + cols: 80, + rows: 24, + process: 'audit-shell', + handleFlowControl: false, + onData(listener: (value: string) => void) { + data.on('data', listener) + return { dispose: () => data.off('data', listener) } + }, + onExit(listener: (value: { exitCode: number; signal?: number }) => void) { + exit.on('exit', listener) + return { dispose: () => exit.off('exit', listener) } + }, + write: vi.fn(), + resize: vi.fn(), + pause: vi.fn(), + resume: vi.fn(), + clear: vi.fn(), + kill: vi.fn(), + destroy: vi.fn() + } satisfies pty.IPty & { destroy: () => void } + return { + process: process_, + emitData: (value: string) => data.emit('data', value), + emitExit: (exitCode: number, signal = 0) => exit.emit('exit', { exitCode, signal }) + } +} + +function start(reportsChildExitStatus = true) { + const native = nativePort() + const env = { PATH: 'audit-path', ORDINARY_FIELD: 'small fixture' } + const args = { + process: native.process, + shellPath: 'audit-shell', + spawnCwd: process.cwd(), + sessionId: 'native-env-retention', + startupAgentRecognition: null, + startupCommandDeliveredInShellArgs: true, + reportsChildExitStatus, + env + } + return { + handle: createDaemonPtySubprocessHandle(args), + native, + refs: { args: new WeakRef(args), env: new WeakRef(env) } + } +} + +async function collect(): Promise { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + for (let round = 0; round < 6; round += 1) { + await new Promise((resolve) => setImmediate(resolve)) + globalThis.gc() + } +} + +describe('native PTY spawn environment lifetime', () => { + it('releases completed spawn arguments while a live handle still forwards data and exit', async () => { + const fixture = start() + try { + await collect() + expect(fixture.refs.args.deref()).toBeUndefined() + expect(fixture.refs.env.deref()).toBeUndefined() + expect(fixture.handle.shellPathEnv).toBe('audit-path') + expect(fixture.handle.startupCommandDeliveredInShellArgs).toBe(true) + expect(fixture.handle.getForegroundProcess?.({ rawFallback: true })).toBe('audit-shell') + + fixture.native.emitData('early-output') + const onData = vi.fn() + const onExit = vi.fn() + fixture.handle.onData(onData) + fixture.handle.onExit(onExit) + expect(onData).toHaveBeenCalledWith('early-output') + fixture.handle.write('input') + expect(fixture.native.process.write).toHaveBeenCalledWith('input') + fixture.native.emitExit(7) + expect(onExit).toHaveBeenCalledWith(7, { kind: 'exited', exitCode: 7 }) + fixture.handle.write('after-exit') + fixture.handle.kill() + fixture.handle.forceKill() + expect(fixture.native.process.write).toHaveBeenCalledOnce() + } finally { + fixture.handle.dispose?.() + fixture.handle.dispose?.() + expect(fixture.native.process.destroy).toHaveBeenCalledOnce() + } + }) + + it.each([true, false])('preserves the spawn-time exit-status fact: %s', async (reportsStatus) => { + const fixture = start(reportsStatus) + try { + await collect() + fixture.native.emitExit(0, 9) + const onExit = vi.fn() + fixture.handle.onExit(onExit) + expect(onExit).toHaveBeenCalledWith( + 0, + reportsStatus + ? { kind: 'signaled', signal: 9 } + : { kind: 'unknown', reason: 'host_status_unavailable' } + ) + } finally { + fixture.handle.dispose?.() + } + }) +}) diff --git a/src/main/daemon/pty-subprocess/subprocess-handle.ts b/src/main/daemon/pty-subprocess/subprocess-handle.ts index 974602dfe84..5d7ef163424 100644 --- a/src/main/daemon/pty-subprocess/subprocess-handle.ts +++ b/src/main/daemon/pty-subprocess/subprocess-handle.ts @@ -23,6 +23,7 @@ export function createDaemonPtySubprocessHandle(args: { sessionId: string startupAgentRecognition: RecognizedAgentProcess | null }): SubprocessHandle { + const reportsChildExitStatus = args.reportsChildExitStatus const proc = args.process // node-pty exposes destroy at runtime but omits it from IPty. const nativeProc = proc as DisposableNativePty @@ -56,7 +57,7 @@ export function createDaemonPtySubprocessHandle(args: { events.acceptExit({ exitCode, signal, - hostReportsChildExitStatus: args.reportsChildExitStatus + hostReportsChildExitStatus: reportsChildExitStatus }) }) From 3c138bd8632f0d2fc72af0d0e50bd8cb4bd98a8b Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:12:19 -0700 Subject: [PATCH 056/168] Skip empty chunks in streamed agent text (#21142) * fix: skip empty chunks in streamed agent text * test: lint empty-delta retention reproducer --------- Co-authored-by: m4air --- .../empty-streamed-delta-retention/README.md | 36 + .../before.config.mjs | 30 + .../electron-results.json | 865 ++++++++++++++++++ .../empty-streamed-delta-retention/fix.patch | 7 + .../node-results.json | 864 +++++++++++++++++ .../reported.patch | 60 ++ .../reproduce.cjs | 66 ++ .../scenario.cjs | 156 ++++ .../source-versions.json | 77 ++ .../sources.cjs | 111 +++ .../validation.json | 50 + .../agent-session-delta-coalescer.ts | 4 +- ...gent-session-empty-delta-retention.test.ts | 126 +++ 13 files changed, 2451 insertions(+), 1 deletion(-) create mode 100644 docs/audits/empty-streamed-delta-retention/README.md create mode 100644 docs/audits/empty-streamed-delta-retention/before.config.mjs create mode 100644 docs/audits/empty-streamed-delta-retention/electron-results.json create mode 100644 docs/audits/empty-streamed-delta-retention/fix.patch create mode 100644 docs/audits/empty-streamed-delta-retention/node-results.json create mode 100644 docs/audits/empty-streamed-delta-retention/reported.patch create mode 100644 docs/audits/empty-streamed-delta-retention/reproduce.cjs create mode 100644 docs/audits/empty-streamed-delta-retention/scenario.cjs create mode 100644 docs/audits/empty-streamed-delta-retention/source-versions.json create mode 100644 docs/audits/empty-streamed-delta-retention/sources.cjs create mode 100644 docs/audits/empty-streamed-delta-retention/validation.json create mode 100644 src/main/native-chat/agent-session-wire/agent-session-empty-delta-retention.test.ts diff --git a/docs/audits/empty-streamed-delta-retention/README.md b/docs/audits/empty-streamed-delta-retention/README.md new file mode 100644 index 00000000000..d872c2ced4e --- /dev/null +++ b/docs/audits/empty-streamed-delta-retention/README.md @@ -0,0 +1,36 @@ +# Empty streamed deltas retain array entries + +The text coalescer charged streamed text by UTF-8 bytes but appended an array entry for every empty delta. A live stream receiving repeated empty updates could retain an increasing number of entries while both byte counters stayed zero. Flushing published a joined string and kept the entries. The actual Codex notification path accepts `delta: ''`; this diagnostic exercises its stream handler and coalescer. + +The fix skips only the empty `chunks.push` operation. Empty-stream creation, snapshots, dirty state, scheduled publication, callback receiver, backpressure and eviction remain unchanged. + +## Reproduce + +```sh +ORCA_BACKGROUND_LAUNCH=1 node docs/audits/empty-streamed-delta-retention/reproduce.cjs +``` + +The runner reverses hash-checked patches in memory and checks every bundled source dependency. It changes no product files and starts no native process or UI. A bounded CRLF control checks source and patch loading. Reports were recorded on Node 26.6.0 and Electron 43.7.0's Node 24.21.0. + +| Control | Before | Fixed | +| -------------------------------------------------------------------- | ------------------------------ | ------------------------- | +| Four batches of 16,384 empty Codex deltas, flushing each batch | 16,384 → 65,536 retained slots | 0 slots after every batch | +| Logical stream count | 1 | 1 | +| Accounted / observed text bytes | 0 / 0 | 0 / 0 | +| Scheduled callbacks / published rows in the complete caller scenario | 6 / 5 | 6 / 5 | +| Append `hé` after empty updates | Same 3-byte text | Same 3-byte text | +| Forget and disposal | Clear retained state | Clear retained state | + +The runner compares the entire recorded publication and scheduling behavior before/after. Controls also cover first-empty snapshots, failed publication and retry, rejection of a new empty key while the previous stream is backpressured, accepted eviction, callback receiver, UTF-8 truncation and an empty update after truncation. The two runtimes each execute four source phases: current/main before and fixed, plus the v1.4.198 coalescer before and with the same narrow guard. + +The permanent regression invokes the actual Codex stream caller. A temporary `Array.prototype.join` observer measures the matching chunk array only during synchronous snapshot creation, then restores the method. The baseline fails with 65,537 slots versus the expected single nonempty prefix; the other 14 coalescer controls pass. All 64 focused compatibility tests pass with the fix. See [validation.json](./validation.json). + +## Source and incident scope + +The current baseline is byte-identical to the coalescer at named main commit `291b4ddd6f1c1af480169885e0fda7f9c78ff053`. The exact v1.4.198 coalescer contains the same unconditional empty append; its surrounding implementation differs. Historical phases replace only that module and use the recorded current Codex caller/dependencies. This is a source overlay, not a packaged historical application replay. [source-versions.json](./source-versions.json) records these distinctions and named caller hashes. + +Claude's generic checkpoint API also uses the coalescer, but its ordinary provider path rejects empty text in `claude-streamed-block-identity.ts` before calling it. This artifact demonstrates the Codex path and preserves Claude compatibility; it does not claim an ordinary Claude trigger. + +Measurement instrumentation reads private map and array cardinalities without changing their contents. These are retained-entry counts, not heap, RSS or byte measurements. The fixture keeps the live stream owned until forget/disposal; it does not establish retention after all owners collect. Nonempty one-byte deltas can still have substantial array overhead within the text-byte allowance, and overflow concatenation has its own transient cost. + +No affected-host data establishes how often Codex emitted empty updates in #19831 or another incident. The finding is a reproducible code-level growth mechanism present in the reported release. It does not attribute an app-scope OOM total to this mechanism or establish its incident magnitude. No remote protocol, process liveness, process termination or terminal ownership behavior changes. diff --git a/docs/audits/empty-streamed-delta-retention/before.config.mjs b/docs/audits/empty-streamed-delta-retention/before.config.mjs new file mode 100644 index 00000000000..5ba89ae6d35 --- /dev/null +++ b/docs/audits/empty-streamed-delta-retention/before.config.mjs @@ -0,0 +1,30 @@ +import base from '../../../config/vitest.config.ts' +import { createRequire } from 'node:module' +import { join } from 'node:path' + +const require = createRequire(import.meta.url) +const { loadSources, root, versions } = require('./sources.cjs') +const baseline = loadSources().baseline +const target = join(root, versions.sourcePath).replaceAll('\\', '/') + +export default { + ...base, + test: { + ...base.test, + include: [ + 'src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.test.ts', + 'src/main/native-chat/agent-session-wire/agent-session-empty-delta-retention.test.ts' + ] + }, + plugins: [ + { + name: 'exact-baseline-coalescer', + enforce: 'pre', + transform(_code, id) { + return id.replaceAll('\\', '/').split('?')[0] === target + ? { code: baseline, map: null } + : null + } + } + ] +} diff --git a/docs/audits/empty-streamed-delta-retention/electron-results.json b/docs/audits/empty-streamed-delta-retention/electron-results.json new file mode 100644 index 00000000000..d4d8ef5a026 --- /dev/null +++ b/docs/audits/empty-streamed-delta-retention/electron-results.json @@ -0,0 +1,865 @@ +{ + "runtime": { + "node": "24.21.0", + "acorn": "8.18.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.2", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "148", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "0.0.0", + "simdjson": "4.6.7", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2025c", + "undici": "7.29.1", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "15.0.245.31-electron.0", + "zlib": "1.3.2.1-motley", + "zstd": "1.6.0", + "electron": "43.7.0", + "chrome": "150.0.7871.250" + }, + "sourceVersions": { + "main": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "reported": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "reportedTag": "v1.4.198" + }, + "scope": "Exact current/main coalescer before/after plus exact v1.4.198 coalescer and same local guard, using current Codex stream callers/dependencies. Not a packaged historical release replay.", + "crlfLoaderControl": { + "reads": 3, + "equal": true + }, + "artifactHashes": { + "sources.cjs": "7902098e4cd0e07825684c778d7d2af74ba1438a55ccf0b9ffe6eb96f76698c8", + "scenario.cjs": "66cfb53b821f631d2f57988c2fab5d28aa0da95fb4a6a3a2b8b643a82d233550", + "reproduce.cjs": "e55b45abda1a7c6078cc3fc867f2553cf9ed6ffea10fbffdb61c99494d797fe1", + "before.config.mjs": "a55e7576be2348edddc137af9c34fca4323bd060d325a1140ab0ede66618c6e9", + "source-versions.json": "1038900fcbc3310ae4b38eb8603d0eea3d6a417c3835a70f3537d27eee6debc9", + "fix.patch": "06f11750dcd64042b7f208b00ae0feb3e0bdea6d5db66b3823ce063fce8ab97a", + "reported.patch": "9d340925bd2a875334a1a4c3ce58c4ba0f977c66bf0c6f5d66848f862fc95d59" + }, + "phases": { + "baseline": { + "sourceSha256": "48828e4ee21858075cbb87ec1caa4a82991a55f80928482615d1df7ec5dc0fb2", + "bundleSha256": "cbdd64b9e21f410645660ac33afe3bede8a58b7680641d5d98883facc0e6a120", + "samples": [ + { + "streams": 1, + "slots": 16384, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 32768, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 49152, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 65536, + "retainedBytes": 0, + "observedBytes": 0 + } + ], + "behavior": { + "scheduled": 6, + "published": 5, + "publications": [ + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "hé" + } + ] + } + } + ], + "directScheduled": 7, + "emitted": [ + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "two", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "two", + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "snapshot": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + } + ], + "truncated": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + }, + "controls": [ + "actual Codex empty notification stream", + "empty snapshot remains present", + "four explicit flushes", + "Unicode text retained", + "forget clears", + "dispose clears", + "first empty publication and retries", + "emit receiver preserved", + "new empty key rejected under backpressure", + "accepted eviction", + "UTF-8 truncation", + "already-truncated empty append keeps no-new-publication behavior" + ] + }, + "fixed": { + "sourceSha256": "7caf0f24250b42e0ac4fe99e93bcbe0a4acb7cb3465fdde6b248d8bf6ba0d2d0", + "bundleSha256": "3e8b7e8430737f4cb0ca8454add7730770d8cd19ab175266d8b7626a059f3912", + "samples": [ + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + } + ], + "behavior": { + "scheduled": 6, + "published": 5, + "publications": [ + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "hé" + } + ] + } + } + ], + "directScheduled": 7, + "emitted": [ + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "two", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "two", + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "snapshot": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + } + ], + "truncated": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + }, + "controls": [ + "actual Codex empty notification stream", + "empty snapshot remains present", + "four explicit flushes", + "Unicode text retained", + "forget clears", + "dispose clears", + "first empty publication and retries", + "emit receiver preserved", + "new empty key rejected under backpressure", + "accepted eviction", + "UTF-8 truncation", + "already-truncated empty append keeps no-new-publication behavior" + ] + }, + "reported": { + "sourceSha256": "bfff14bd820a2d7be90e82db6403d2415c0fb3de998cf61d7c3830ff4c415605", + "bundleSha256": "c8e429f7f35a4a61c3189fcedcac5abc9658dab841ea927cf285f90eeccd381c", + "samples": [ + { + "streams": 1, + "slots": 16384, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 32768, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 49152, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 65536, + "retainedBytes": 0, + "observedBytes": 0 + } + ], + "behavior": { + "scheduled": 6, + "published": 5, + "publications": [ + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "hé" + } + ] + } + } + ], + "directScheduled": 7, + "emitted": [ + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "two", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "two", + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "snapshot": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + } + ], + "truncated": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + }, + "controls": [ + "actual Codex empty notification stream", + "empty snapshot remains present", + "four explicit flushes", + "Unicode text retained", + "forget clears", + "dispose clears", + "first empty publication and retries", + "emit receiver preserved", + "new empty key rejected under backpressure", + "accepted eviction", + "UTF-8 truncation", + "already-truncated empty append keeps no-new-publication behavior" + ] + }, + "reportedFixed": { + "sourceSha256": "2f00aec9b7a4fb24cad4e01d1bf7e1d0b881076f9760b322cdf72c39594ae89c", + "bundleSha256": "3749aae7c5eb00f3eea98c66a7b0c4e0e7114cde1d983d89951bd4c21b33e680", + "samples": [ + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + } + ], + "behavior": { + "scheduled": 6, + "published": 5, + "publications": [ + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "hé" + } + ] + } + } + ], + "directScheduled": 7, + "emitted": [ + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "two", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "two", + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "snapshot": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + } + ], + "truncated": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + }, + "controls": [ + "actual Codex empty notification stream", + "empty snapshot remains present", + "four explicit flushes", + "Unicode text retained", + "forget clears", + "dispose clears", + "first empty publication and retries", + "emit receiver preserved", + "new empty key rejected under backpressure", + "accepted eviction", + "UTF-8 truncation", + "already-truncated empty append keeps no-new-publication behavior" + ] + } + }, + "measurement": "Read-only closure observes private Map and chunk-array cardinalities in source overlay. No heap/RSS measurement, native process or affected-host inference." +} diff --git a/docs/audits/empty-streamed-delta-retention/fix.patch b/docs/audits/empty-streamed-delta-retention/fix.patch new file mode 100644 index 00000000000..b450bfc909f --- /dev/null +++ b/docs/audits/empty-streamed-delta-retention/fix.patch @@ -0,0 +1,7 @@ +--- a/src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts ++++ b/src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts +@@ -233 +233,3 @@ +- current.push(delta) ++ if (delta.length > 0) { ++ current.push(delta) ++ } diff --git a/docs/audits/empty-streamed-delta-retention/node-results.json b/docs/audits/empty-streamed-delta-retention/node-results.json new file mode 100644 index 00000000000..c614c1675e7 --- /dev/null +++ b/docs/audits/empty-streamed-delta-retention/node-results.json @@ -0,0 +1,864 @@ +{ + "runtime": { + "node": "26.6.0", + "acorn": "8.17.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.3", + "libffi": "3.7.1", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "147", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "3.6.3", + "simdjson": "4.6.6", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2026a", + "undici": "8.9.0", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "14.6.202.34-node.26", + "zlib": "1.2.12", + "zstd": "1.5.7" + }, + "sourceVersions": { + "main": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "reported": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "reportedTag": "v1.4.198" + }, + "scope": "Exact current/main coalescer before/after plus exact v1.4.198 coalescer and same local guard, using current Codex stream callers/dependencies. Not a packaged historical release replay.", + "crlfLoaderControl": { + "reads": 3, + "equal": true + }, + "artifactHashes": { + "sources.cjs": "7902098e4cd0e07825684c778d7d2af74ba1438a55ccf0b9ffe6eb96f76698c8", + "scenario.cjs": "66cfb53b821f631d2f57988c2fab5d28aa0da95fb4a6a3a2b8b643a82d233550", + "reproduce.cjs": "e55b45abda1a7c6078cc3fc867f2553cf9ed6ffea10fbffdb61c99494d797fe1", + "before.config.mjs": "a55e7576be2348edddc137af9c34fca4323bd060d325a1140ab0ede66618c6e9", + "source-versions.json": "1038900fcbc3310ae4b38eb8603d0eea3d6a417c3835a70f3537d27eee6debc9", + "fix.patch": "06f11750dcd64042b7f208b00ae0feb3e0bdea6d5db66b3823ce063fce8ab97a", + "reported.patch": "9d340925bd2a875334a1a4c3ce58c4ba0f977c66bf0c6f5d66848f862fc95d59" + }, + "phases": { + "baseline": { + "sourceSha256": "48828e4ee21858075cbb87ec1caa4a82991a55f80928482615d1df7ec5dc0fb2", + "bundleSha256": "cbdd64b9e21f410645660ac33afe3bede8a58b7680641d5d98883facc0e6a120", + "samples": [ + { + "streams": 1, + "slots": 16384, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 32768, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 49152, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 65536, + "retainedBytes": 0, + "observedBytes": 0 + } + ], + "behavior": { + "scheduled": 6, + "published": 5, + "publications": [ + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "hé" + } + ] + } + } + ], + "directScheduled": 7, + "emitted": [ + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "two", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "two", + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "snapshot": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + } + ], + "truncated": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + }, + "controls": [ + "actual Codex empty notification stream", + "empty snapshot remains present", + "four explicit flushes", + "Unicode text retained", + "forget clears", + "dispose clears", + "first empty publication and retries", + "emit receiver preserved", + "new empty key rejected under backpressure", + "accepted eviction", + "UTF-8 truncation", + "already-truncated empty append keeps no-new-publication behavior" + ] + }, + "fixed": { + "sourceSha256": "7caf0f24250b42e0ac4fe99e93bcbe0a4acb7cb3465fdde6b248d8bf6ba0d2d0", + "bundleSha256": "3e8b7e8430737f4cb0ca8454add7730770d8cd19ab175266d8b7626a059f3912", + "samples": [ + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + } + ], + "behavior": { + "scheduled": 6, + "published": 5, + "publications": [ + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "hé" + } + ] + } + } + ], + "directScheduled": 7, + "emitted": [ + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "two", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "two", + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "snapshot": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + } + ], + "truncated": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + }, + "controls": [ + "actual Codex empty notification stream", + "empty snapshot remains present", + "four explicit flushes", + "Unicode text retained", + "forget clears", + "dispose clears", + "first empty publication and retries", + "emit receiver preserved", + "new empty key rejected under backpressure", + "accepted eviction", + "UTF-8 truncation", + "already-truncated empty append keeps no-new-publication behavior" + ] + }, + "reported": { + "sourceSha256": "bfff14bd820a2d7be90e82db6403d2415c0fb3de998cf61d7c3830ff4c415605", + "bundleSha256": "c8e429f7f35a4a61c3189fcedcac5abc9658dab841ea927cf285f90eeccd381c", + "samples": [ + { + "streams": 1, + "slots": 16384, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 32768, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 49152, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 65536, + "retainedBytes": 0, + "observedBytes": 0 + } + ], + "behavior": { + "scheduled": 6, + "published": 5, + "publications": [ + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "hé" + } + ] + } + } + ], + "directScheduled": 7, + "emitted": [ + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "two", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "two", + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "snapshot": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + } + ], + "truncated": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + }, + "controls": [ + "actual Codex empty notification stream", + "empty snapshot remains present", + "four explicit flushes", + "Unicode text retained", + "forget clears", + "dispose clears", + "first empty publication and retries", + "emit receiver preserved", + "new empty key rejected under backpressure", + "accepted eviction", + "UTF-8 truncation", + "already-truncated empty append keeps no-new-publication behavior" + ] + }, + "reportedFixed": { + "sourceSha256": "2f00aec9b7a4fb24cad4e01d1bf7e1d0b881076f9760b322cdf72c39594ae89c", + "bundleSha256": "3749aae7c5eb00f3eea98c66a7b0c4e0e7114cde1d983d89951bd4c21b33e680", + "samples": [ + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + }, + { + "streams": 1, + "slots": 0, + "retainedBytes": 0, + "observedBytes": 0 + } + ], + "behavior": { + "scheduled": 6, + "published": 5, + "publications": [ + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "" + } + ] + } + }, + { + "identity": { + "provider": "codex", + "threadId": "thread-a", + "turnId": "turn-a", + "ordinal": 0 + }, + "body": { + "kind": "message", + "role": "assistant", + "blocks": [ + { + "type": "text", + "text": "hé" + } + ] + } + } + ], + "directScheduled": 7, + "emitted": [ + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "one", + "text": "unchanged", + "snapshot": { + "text": "unchanged", + "observedBytes": 9, + "truncated": false + } + }, + { + "key": "two", + "text": "", + "snapshot": { + "text": "", + "observedBytes": 0, + "truncated": false + } + }, + { + "key": "two", + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "snapshot": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + } + ], + "truncated": { + "text": "😀😀😀😀😀😀😀\n[Orca: streamed output truncated]", + "observedBytes": 400, + "truncated": true + } + }, + "controls": [ + "actual Codex empty notification stream", + "empty snapshot remains present", + "four explicit flushes", + "Unicode text retained", + "forget clears", + "dispose clears", + "first empty publication and retries", + "emit receiver preserved", + "new empty key rejected under backpressure", + "accepted eviction", + "UTF-8 truncation", + "already-truncated empty append keeps no-new-publication behavior" + ] + } + }, + "measurement": "Read-only closure observes private Map and chunk-array cardinalities in source overlay. No heap/RSS measurement, native process or affected-host inference." +} diff --git a/docs/audits/empty-streamed-delta-retention/reported.patch b/docs/audits/empty-streamed-delta-retention/reported.patch new file mode 100644 index 00000000000..51ded40b8a6 --- /dev/null +++ b/docs/audits/empty-streamed-delta-retention/reported.patch @@ -0,0 +1,60 @@ +--- a/src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts ++++ b/src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts +@@ -39,0 +40,2 @@ ++ /** The caller byte-bounds protected metadata; only ordinary streams use the count cap. */ ++ isProtected?: (key: string) => boolean +@@ -86,2 +88 @@ +- const streamOrder = new Map() +- let nextOrder = 0 ++ const evictable = new Set() +@@ -133,2 +134,2 @@ +- if (streams.size >= maxStreams) { +- const oldest = [...streamOrder.entries()].sort((a, b) => a[1] - b[1])[0]?.[0] ++ while (!deps.isProtected?.(key) && evictable.size >= maxStreams) { ++ const oldest = evictable.values().next().value +@@ -135,0 +137,4 @@ ++ if (deps.isProtected?.(oldest)) { ++ evictable.delete(oldest) ++ continue ++ } +@@ -146 +151 @@ +- streamOrder.delete(oldest) ++ evictable.delete(oldest) +@@ -147,0 +153 @@ ++ break +@@ -156 +162,5 @@ +- streamOrder.set(key, nextOrder++) ++ if (!deps.isProtected?.(key)) { ++ evictable.add(key) ++ } ++ } else if (deps.isProtected?.(key)) { ++ evictable.delete(key) +@@ -158 +168,2 @@ +- stream.observedBytes += Buffer.byteLength(delta, 'utf8') ++ const deltaBytes = Buffer.byteLength(delta, 'utf8') ++ stream.observedBytes += deltaBytes +@@ -165,0 +177 @@ ++ deltaBytes, +@@ -187 +199 @@ +- streamOrder.delete(key) ++ evictable.delete(key) +@@ -194 +206 @@ +- streamOrder.clear() ++ evictable.clear() +@@ -213,0 +226 @@ ++ deltaBytes: number, +@@ -217,2 +230 @@ +- const deltaBuffer = Buffer.from(delta, 'utf8') +- if (deltaBuffer.byteLength <= available) { ++ if (deltaBytes <= available) { +@@ -221 +233,3 @@ +- current.push(delta) ++ if (delta.length > 0) { ++ current.push(delta) ++ } +@@ -224 +238 @@ +- retainedBytes: currentBytes + deltaBuffer.byteLength, ++ retainedBytes: currentBytes + deltaBytes, +@@ -232 +246 @@ +- deltaBuffer ++ Buffer.from(delta, 'utf8') diff --git a/docs/audits/empty-streamed-delta-retention/reproduce.cjs b/docs/audits/empty-streamed-delta-retention/reproduce.cjs new file mode 100644 index 00000000000..6a947ca003e --- /dev/null +++ b/docs/audits/empty-streamed-delta-retention/reproduce.cjs @@ -0,0 +1,66 @@ +const assert = require('node:assert/strict') +const { readFileSync, writeFileSync } = require('node:fs') +const path = require('node:path') +const { scenario } = require('./scenario.cjs') +const { loadSources, sha, versions } = require('./sources.cjs') + +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1', 'Run with ORCA_BACKGROUND_LAUNCH=1') + +;(async () => { + const canonical = loadSources() + let crlfReads = 0 + const crlf = loadSources((file) => { + crlfReads += 1 + return readFileSync(file, 'utf8').replaceAll('\r\n', '\n').replaceAll('\n', '\r\n') + }) + assert.deepEqual(crlf, canonical) + assert.equal(crlfReads, 3) + const phases = {} + for (const phase of ['baseline', 'fixed', 'reported', 'reportedFixed']) { + phases[phase] = await scenario(phase) + } + assert.deepEqual(phases.baseline.behavior, phases.fixed.behavior) + assert.deepEqual(phases.reported.behavior, phases.reportedFixed.behavior) + const artifactHashes = Object.fromEntries( + [ + 'sources.cjs', + 'scenario.cjs', + 'reproduce.cjs', + 'before.config.mjs', + 'source-versions.json', + 'fix.patch', + 'reported.patch' + ].map((file) => [file, sha(readFileSync(path.join(__dirname, file)))]) + ) + const result = { + runtime: process.versions, + sourceVersions: versions.namedReferences, + scope: versions.scope, + crlfLoaderControl: { reads: crlfReads, equal: true }, + artifactHashes, + phases, + measurement: + 'Read-only closure observes private Map and chunk-array cardinalities in source overlay. No heap/RSS measurement, native process or affected-host inference.' + } + const output = process.argv[2] ?? path.join(__dirname, 'node-results.json') + writeFileSync(output, `${JSON.stringify(result, null, 2)}\n`) + console.log( + JSON.stringify({ + output, + phases: Object.fromEntries( + Object.entries(phases).map(([phase, result]) => [ + phase, + { + samples: result.samples, + scheduled: result.behavior.scheduled, + published: result.behavior.published + } + ]) + ), + behaviorEqual: true + }) + ) +})().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/docs/audits/empty-streamed-delta-retention/scenario.cjs b/docs/audits/empty-streamed-delta-retention/scenario.cjs new file mode 100644 index 00000000000..943d8130062 --- /dev/null +++ b/docs/audits/empty-streamed-delta-retention/scenario.cjs @@ -0,0 +1,156 @@ +const assert = require('node:assert/strict') +const { load } = require('./sources.cjs') + +async function scenario(phase) { + const readers = [] + globalThis.__orcaEmptyDeltaReaders = readers + const { + createCodexStructuredItemStreams, + createAgentSessionDeltaCoalescer, + sourceSha256, + bundleSha256 + } = await load(phase) + const fixed = phase === 'fixed' || phase === 'reportedFixed' + let scheduled = 0 + let published = 0 + const publications = [] + const streams = createCodexStructuredItemStreams({ + sink: { + appendItem(identity, body) { + published += 1 + publications.push({ identity, body }) + }, + publish() {} + }, + identityFor: () => ({ provider: 'codex', threadId: 'thread-a', turnId: 'turn-a', ordinal: 0 }), + schedule: () => { + scheduled += 1 + return () => {} + } + }) + assert.equal(readers.length, 1) + const read = readers[0] + const samples = [] + for (let batch = 0; batch < 4; batch += 1) { + for (let index = 0; index < 16384; index += 1) { + assert.deepEqual( + streams.handle('thread-a', 'item/agentMessage/delta', { itemId: 'item-a', delta: '' }), + { handled: true, admission: { accepted: true } } + ) + } + assert.equal(streams.flush(), true) + samples.push(read()) + assert.deepEqual(streams.snapshot('thread-a', 'item-a'), { + text: '', + observedBytes: 0, + truncated: false + }) + } + assert.equal(read().slots, fixed ? 0 : 65536) + assert.equal(read().retainedBytes, 0) + assert.equal(read().observedBytes, 0) + streams.handle('thread-a', 'item/agentMessage/delta', { itemId: 'item-a', delta: 'hé' }) + assert.equal(streams.flush(), true) + assert.deepEqual(streams.snapshot('thread-a', 'item-a'), { + text: 'hé', + observedBytes: 3, + truncated: false + }) + assert.equal(read().slots, fixed ? 1 : 65537) + streams.forget('thread-a', 'item-a') + assert.deepEqual(read(), { streams: 0, slots: 0, retainedBytes: 0, observedBytes: 0 }) + streams.handle('thread-a', 'item/agentMessage/delta', { + itemId: 'item-a', + delta: 'retained until dispose' + }) + streams.dispose() + assert.deepEqual(read(), { streams: 0, slots: 0, retainedBytes: 0, observedBytes: 0 }) + + let accepting = false + const pending = new Set() + const emitted = [] + let directScheduled = 0 + const deps = { + emit(key, text, snapshot) { + assert.equal(this, deps) + if (!accepting) { + return false + } + emitted.push({ key, text, snapshot }) + return true + }, + schedule(run) { + directScheduled += 1 + pending.add(run) + return () => pending.delete(run) + }, + maxStreams: 1, + maxRetainedBytes: 64, + maxTotalRetainedBytes: 64 + } + const direct = createAgentSessionDeltaCoalescer(deps) + assert.equal(direct.append('one', ''), true) + assert.deepEqual(direct.snapshot('one'), { text: '', observedBytes: 0, truncated: false }) + assert.equal(pending.size, 1) + assert.equal(direct.flushAll(), false) + assert.equal(pending.size, 1) + assert.equal(direct.append('two', ''), false) + assert.equal(direct.snapshot('two'), null) + accepting = true + assert.equal(direct.flushAll(), true) + assert.equal(pending.size, 0) + assert.equal(direct.append('one', ''), true) + assert.equal(direct.flushAll(), true) + assert.equal(emitted.length, 2) + assert.equal(direct.append('one', 'unchanged'), true) + assert.equal(direct.flushAll(), true) + accepting = false + direct.append('one', '') + assert.equal(direct.append('two', ''), false) + assert.deepEqual(direct.snapshot('one'), { + text: 'unchanged', + observedBytes: 9, + truncated: false + }) + accepting = true + assert.equal(direct.append('two', ''), true) + assert.equal(direct.snapshot('one'), null) + assert.equal(direct.flushAll(), true) + direct.append('two', '😀'.repeat(100)) + assert.equal(direct.flushAll(), true) + const truncated = direct.snapshot('two') + assert.ok(Buffer.byteLength(truncated.text, 'utf8') <= 64) + assert.equal(truncated.truncated, true) + assert.equal(truncated.observedBytes, 400) + const publicationsBeforeEmpty = emitted.length + direct.append('two', '') + assert.equal(direct.flushAll(), true) + assert.equal(emitted.length, publicationsBeforeEmpty) + assert.deepEqual(direct.snapshot('two'), truncated) + direct.dispose() + assert.equal(pending.size, 0) + assert.deepEqual(readers[1](), { streams: 0, slots: 0, retainedBytes: 0, observedBytes: 0 }) + delete globalThis.__orcaEmptyDeltaReaders + return { + sourceSha256, + bundleSha256, + samples, + behavior: { scheduled, published, publications, directScheduled, emitted, truncated }, + controls: [ + 'actual Codex empty notification stream', + 'empty snapshot remains present', + 'four explicit flushes', + 'Unicode text retained', + 'forget clears', + 'dispose clears', + 'first empty publication and retries', + 'emit receiver preserved', + 'new empty key rejected under backpressure', + 'accepted eviction', + 'UTF-8 truncation', + 'already-truncated empty append keeps no-new-publication behavior' + ] + } +} + +module.exports = { scenario } diff --git a/docs/audits/empty-streamed-delta-retention/source-versions.json b/docs/audits/empty-streamed-delta-retention/source-versions.json new file mode 100644 index 00000000000..29f6bb0c1f2 --- /dev/null +++ b/docs/audits/empty-streamed-delta-retention/source-versions.json @@ -0,0 +1,77 @@ +{ + "sourcePath": "src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts", + "canonicalization": "CRLF to LF", + "baselineSha256": "48828e4ee21858075cbb87ec1caa4a82991a55f80928482615d1df7ec5dc0fb2", + "fixedSha256": "7caf0f24250b42e0ac4fe99e93bcbe0a4acb7cb3465fdde6b248d8bf6ba0d2d0", + "reportedSha256": "bfff14bd820a2d7be90e82db6403d2415c0fb3de998cf61d7c3830ff4c415605", + "reportedFixedSha256": "2f00aec9b7a4fb24cad4e01d1bf7e1d0b881076f9760b322cdf72c39594ae89c", + "namedReferences": { + "main": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "reported": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "reportedTag": "v1.4.198" + }, + "commonDependencies": { + "src/shared/agent-session-journal-item-key.ts": "09ebe4758e3f38b5b7591f07f7b4dbfd04c7cc01713c9b0afe03b07b656d1fe8", + "src/main/codex/codex-command-lifecycle.ts": "7b480d3b111304e3caff71768fb3b11ab6c36e1fa8f5b0440623a77cac2382c2", + "src/shared/native-chat-turn-status.ts": "4ce2b64fa7818106814c135e5daf77bb183f601c35f9f08e5b7ff847fba01378", + "src/shared/native-chat-tool-identity.ts": "8c98eec1a53b26a67ed48a0859c5863f5accbe67e013c4eb5a0f8b32441ee925", + "src/main/codex/codex-structured-item-stream-bounds.ts": "88e9efa8a9c163905f1a5eb5eaf316fba2f194d76c5a94ed7f55636cc0d74ae0", + "src/main/codex/codex-item-stream-retention.ts": "66d1f1d51ed6711e20f81698b14be2d003e899dbc587ac1306b6a656cf951b96", + "src/main/native-chat/agent-session-journal/journal-payload-bounds.ts": "a421da6c6eee9346746a69f8ba7ff966ce81969dfa0a93f58784cac27cf8cdad", + "src/main/codex/codex-goal-journal-rows.ts": "415846ca105300349778da7637216ca6ce978c7036219f6b3038f7228ba8724e", + "src/main/codex/codex-subagent-activity.ts": "5cdf18a4ea2c68178a67a845d4c95672a0d94bb282e8e15c3a930c6dd59151e4", + "src/main/native-chat/agent-session-wire/provider-frame-disposition.ts": "f24008cebb1b8a77b0cbd091e49140a4bf99c676bc61eedeb0a82f1263244a7c", + "src/main/native-chat/agent-session-wire/unhandled-provider-frame.ts": "f8169a024a573adfc97e4eb8c99ecc4e80f82e9f94a04a29011a6b8051cc7626", + "src/shared/raster-image-dimensions.ts": "0d7462b8b2e53bfcdcbd32803edcfb85ed0063f56d45cf230fb59aac4783d063", + "src/shared/raster-image-preview-limits.ts": "f2d836b354b3951912f1ec689d84b82aa1aade89ed8e9c6f76be2321d600e9a7", + "src/shared/raster-image-base64-preview.ts": "89baf40f4637615456888e0f35115517a8445a3b1cb640b7bc495dd9a98fcedb", + "src/shared/image-data-uri.ts": "3eb9d2bda499b8f10c74783f9de766041469b140a9b96b2b5be7d97528688ce0", + "src/main/codex/codex-item-field-readers.ts": "aa4b178c562a63995b7de63c6f5daaf7b6b610fb163fb0a357cc230314076323", + "src/main/codex/codex-image-item-translation.ts": "c4f88236c707472cbcfcdfb1a04e51c738f8741aa5847a33f730759499522f5e", + "src/main/codex/codex-command-action-class.ts": "93b7bceb66a9e5bfae680e018b61bddb5c1117fe26243088ceccc8738841f966", + "src/main/codex/codex-thread-item-identity.ts": "04b98b367a4d658fb759ebc4fc73ea4ae731ae3f238f191c8ce002b2a2c9e2a8", + "src/main/codex/codex-turn-ordinals.ts": "9b7cf66986235bcacd6c198950452c7f40bd559a3a0ce20995aac496a1e32d7f", + "src/main/codex/codex-structured-item-translation.ts": "8ab9debb279baf3a5043ffe754c8911755c6e4ca9edfc0a094671c4d5d166639", + "src/main/codex/codex-structured-item-stream-events.ts": "da2a9da9025af354ce705a190badadf822f9ac3b403eef04aa0ef8aaf6f023de", + "src/main/codex/codex-structured-item-streams.ts": "b70c3306e8bec3587342afc3bc156f40903513fa7ff3865b217ba4cc912cd89f" + }, + "callerSourceHashes": [ + { + "path": "src/main/codex/codex-structured-item-streams.ts", + "working": "b70c3306e8bec3587342afc3bc156f40903513fa7ff3865b217ba4cc912cd89f", + "main": "b70c3306e8bec3587342afc3bc156f40903513fa7ff3865b217ba4cc912cd89f", + "reported": "0f05fd8232d5f9b7a928abdd97ea04846ffade9512d61371f120dbcff00c09b6" + }, + { + "path": "src/main/codex/codex-structured-journal-translation.ts", + "working": "56e17b9649471e804d29d1d54a3e14b4298ca8af2e25c4759dc37fce000b06a1", + "main": "56e17b9649471e804d29d1d54a3e14b4298ca8af2e25c4759dc37fce000b06a1", + "reported": "7a6409082b977e481b137b19f446ee3e17d530f4f72c4d141263a49d3ca7722c" + }, + { + "path": "src/main/codex/codex-structured-provider-events.ts", + "working": "a411dc378e521ea8fc915e2a2a21476341a30f440e8233ae5649205d2a1e2e44", + "main": "a411dc378e521ea8fc915e2a2a21476341a30f440e8233ae5649205d2a1e2e44", + "reported": "69af127e2d2ee6b6d648028f16f3e6d25ea45ed61919d14642a7a554e3ad05a5" + }, + { + "path": "src/main/codex/codex-app-server-notification-schema.ts", + "working": "d551b5fb47aad42fb09d9cee2c68e1e4438d209e13869875fed68c3679ef66c8", + "main": "d551b5fb47aad42fb09d9cee2c68e1e4438d209e13869875fed68c3679ef66c8", + "reported": "d551b5fb47aad42fb09d9cee2c68e1e4438d209e13869875fed68c3679ef66c8" + }, + { + "path": "src/main/claude/claude-streamed-text-checkpoints.ts", + "working": "a0e435f5fcf73f37e48e5363f8f9245d992a0d6c0a80abdf5cc5c72b2595fe6d", + "main": "a0e435f5fcf73f37e48e5363f8f9245d992a0d6c0a80abdf5cc5c72b2595fe6d", + "reported": "a0e435f5fcf73f37e48e5363f8f9245d992a0d6c0a80abdf5cc5c72b2595fe6d" + }, + { + "path": "src/main/claude/claude-streamed-block-identity.ts", + "working": "8bc2d4a18fdecd4e27fdf2e8ea3274fcf2e3353edb75607bd0d1133546bfbdc0", + "main": "8bc2d4a18fdecd4e27fdf2e8ea3274fcf2e3353edb75607bd0d1133546bfbdc0", + "reported": "8bc2d4a18fdecd4e27fdf2e8ea3274fcf2e3353edb75607bd0d1133546bfbdc0" + } + ], + "scope": "Exact current/main coalescer before/after plus exact v1.4.198 coalescer and same local guard, using current Codex stream callers/dependencies. Not a packaged historical release replay." +} diff --git a/docs/audits/empty-streamed-delta-retention/sources.cjs b/docs/audits/empty-streamed-delta-retention/sources.cjs new file mode 100644 index 00000000000..36ec31bd52f --- /dev/null +++ b/docs/audits/empty-streamed-delta-retention/sources.cjs @@ -0,0 +1,111 @@ +const assert = require('node:assert/strict') +const { createHash } = require('node:crypto') +const { readFileSync } = require('node:fs') +const path = require('node:path') +const Module = require('node:module') +const esbuild = require('esbuild') +const { applyPatch, parsePatch, reversePatch } = require('diff') + +const root = path.resolve(__dirname, '../../..') +const canonical = (value) => value.replaceAll('\r\n', '\n') +const sha = (value) => createHash('sha256').update(value).digest('hex') +const readText = (file) => canonical(readFileSync(file, 'utf8')) +const versions = JSON.parse(readText(path.join(__dirname, 'source-versions.json'))) + +function loadSources(read = readText) { + const fixed = canonical(read(path.join(root, versions.sourcePath))) + assert.equal(sha(fixed), versions.fixedSha256, 'Fixed source drift') + const reverse = (name) => { + const patches = parsePatch(canonical(read(path.join(__dirname, name)))) + assert.equal(patches.length, 1) + assert.equal(patches[0].newFileName, `b/${versions.sourcePath}`) + const source = applyPatch(fixed, reversePatch(patches[0])) + assert.notEqual(source, false) + return source + } + const baseline = reverse('fix.patch') + const reported = reverse('reported.patch') + const marker = ' current.push(delta)' + assert.equal(reported.split(marker).length, 2) + const reportedFixed = reported.replace( + marker, + ' if (delta.length > 0) {\n current.push(delta)\n }' + ) + assert.equal(sha(baseline), versions.baselineSha256, 'Baseline source drift') + assert.equal(sha(reported), versions.reportedSha256, 'Reported source drift') + assert.equal(sha(reportedFixed), versions.reportedFixedSha256) + return { baseline, fixed, reported, reportedFixed } +} + +async function load(phase) { + const sources = loadSources() + assert.ok(Object.hasOwn(sources, phase)) + for (const [file, expected] of Object.entries(versions.commonDependencies)) { + assert.equal(sha(readText(path.join(root, file))), expected, `Dependency drift: ${file}`) + } + for (const caller of versions.callerSourceHashes) { + assert.equal( + sha(readText(path.join(root, caller.path))), + caller.working, + `Caller drift: ${caller.path}` + ) + } + const marker = ' const flushKey = (key: string): boolean => {' + const source = sources[phase] + assert.equal(source.split(marker).length, 2) + // Measurement only reads cardinalities; it never changes stream ownership or contents. + const measured = source.replace( + marker, + ` globalThis.__orcaEmptyDeltaReaders.push(() => ({ + streams: streams.size, + slots: [...streams.values()].reduce((count, stream) => count + stream.chunks.length, 0), + retainedBytes: totalRetainedBytes, + observedBytes: [...streams.values()].reduce((count, stream) => count + stream.observedBytes, 0) + }))\n${marker}` + ) + const build = await esbuild.build({ + stdin: { + contents: + "export { createCodexStructuredItemStreams } from './src/main/codex/codex-structured-item-streams'; export { createAgentSessionDeltaCoalescer } from './src/main/native-chat/agent-session-wire/agent-session-delta-coalescer'", + resolveDir: root, + loader: 'ts' + }, + absWorkingDir: root, + bundle: true, + platform: 'node', + format: 'cjs', + packages: 'external', + write: false, + metafile: true, + plugins: [ + { + name: 'read-private-array-cardinality', + setup(builder) { + builder.onLoad({ filter: /agent-session-delta-coalescer\.ts$/ }, (args) => { + assert.equal(args.path, path.join(root, versions.sourcePath)) + return { contents: measured, loader: 'ts' } + }) + } + } + ] + }) + const actualInputs = Object.keys(build.metafile.inputs) + .filter((file) => file.startsWith('src/')) + .sort() + assert.deepEqual( + actualInputs, + [...Object.keys(versions.commonDependencies), versions.sourcePath].sort() + ) + const filename = path.join(__dirname, `in-memory-${phase}.cjs`) + const loaded = new Module(filename, module) + loaded.filename = filename + loaded.paths = Module._nodeModulePaths(root) + loaded._compile(build.outputFiles[0].text, filename) + return { + ...loaded.exports, + sourceSha256: sha(source), + bundleSha256: sha(build.outputFiles[0].contents) + } +} + +module.exports = { load, loadSources, root, sha, versions } diff --git a/docs/audits/empty-streamed-delta-retention/validation.json b/docs/audits/empty-streamed-delta-retention/validation.json new file mode 100644 index 00000000000..b53bf94f6ef --- /dev/null +++ b/docs/audits/empty-streamed-delta-retention/validation.json @@ -0,0 +1,50 @@ +{ + "reviewedHead": "a8c4bed3fa4191d731bb28826d00318f18a5db0a", + "backgroundLaunch": "ORCA_BACKGROUND_LAUNCH=1 on every check", + "productHashes": { + "src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts": "7caf0f24250b42e0ac4fe99e93bcbe0a4acb7cb3465fdde6b248d8bf6ba0d2d0", + "src/main/native-chat/agent-session-wire/agent-session-empty-delta-retention.test.ts": "2e4ed51b8c205e650cd0d3d4fcb968fdf8f509be4c4013ca1dc6e70b59703a55" + }, + "focusedTests": { + "files": 6, + "passed": 64, + "failed": 0, + "paths": [ + "src/main/native-chat/agent-session-wire/agent-session-empty-delta-retention.test.ts", + "src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.test.ts", + "src/main/codex/codex-persistent-command-retention.test.ts", + "src/main/codex/codex-structured-journal-translation.test.ts", + "src/main/codex/codex-structured-journal-translation-streams.test.ts", + "src/main/claude/claude-streamed-text-checkpoints.test.ts" + ] + }, + "baselineTests": { + "config": "docs/audits/empty-streamed-delta-retention/before.config.mjs", + "passed": 14, + "expectedFailures": 1, + "failureName": "empty streamed deltas does not retain empty array slots through repeated Codex publications", + "assertion": "expected 65537 to be 1", + "scope": "Only the new retained-slot regression fails; all publication/backpressure controls pass." + }, + "checks": { + "nodeTypecheck": "passed: pnpm tc:node", + "ordinaryLint": "passed: oxlint on two product files", + "typeAwareLint": "passed: oxlint --type-aware --config config/oxlint-code-quality-type-aware.json --deny-warnings on two product files", + "changedQuality": "passed: pnpm run check:code-quality:changed HEAD, two files, zero new findings", + "format": "passed: oxfmt product and artifact files", + "diffWhitespace": "git diff --check on exact product paths; zero-context historical/fix patches" + }, + "proofReports": { + "node-results.json": "a858b703a1c94c2bc4bc817f244a76fb84bd4ea4cb2dbbb6688acef91d25905d", + "electron-results.json": "1ba7c0dd598244cc8c5af9643823470198bf4409deae60939f13dc5dc4b716d5" + }, + "independentReview": "rpc_queue_retention reviewed actual source, tests, named hashes and behavior parity; separately reran all 15 coalescer/new tests.", + "ciArtifactCorrection": { + "pullRequest": 21142, + "failedHead": "9ed7c4f5c880ab64dda8a1a8a04ca8455af42b5d", + "failedJob": "https://github.com/stablyai/orca/actions/runs/35178713175/job/105066129816", + "cause": "One-line if in scenario.cjs lacked braces. Product-only local quality omitted durable proof sources; CI correctly rejected it.", + "change": "Add braces; product code unchanged. Rerun both actual-source runtime reports and all five quality scan configurations over all six published code files with --no-ignore.", + "result": "Both runtime proofs and all five quality scans pass. CI status remains separately recorded at the observed head." + } +} diff --git a/src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts b/src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts index a3b32ca24f0..1d5608150ef 100644 --- a/src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts +++ b/src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.ts @@ -230,7 +230,9 @@ function appendWithinUtf8ByteLimit( if (deltaBytes <= available) { // The caller owns the per-stream array; append in place so each token is // amortized O(1) instead of copying the complete prefix on every delta. - current.push(delta) + if (delta.length > 0) { + current.push(delta) + } return { chunks: current, retainedBytes: currentBytes + deltaBytes, diff --git a/src/main/native-chat/agent-session-wire/agent-session-empty-delta-retention.test.ts b/src/main/native-chat/agent-session-wire/agent-session-empty-delta-retention.test.ts new file mode 100644 index 00000000000..4e2161b19a8 --- /dev/null +++ b/src/main/native-chat/agent-session-wire/agent-session-empty-delta-retention.test.ts @@ -0,0 +1,126 @@ +import { describe, expect, it, vi } from 'vitest' +import { createCodexStructuredItemStreams } from '../../codex/codex-structured-item-streams' +import { createAgentSessionDeltaCoalescer } from './agent-session-delta-coalescer' + +describe('empty streamed deltas', () => { + it('does not retain empty array slots through repeated Codex publications', () => { + const prefix = 'empty-delta-retention-prefix' + const streams = createCodexStructuredItemStreams({ + sink: { appendItem() {}, appendTombstone() {}, publish() {} }, + identityFor: () => ({ provider: 'codex', threadId: 'thread', turnId: 'turn', ordinal: 0 }), + schedule: () => () => {} + }) + const append = (delta: string) => + streams.handle('thread', 'item/agentMessage/delta', { itemId: 'item', delta }) + append(prefix) + try { + for (let batch = 0; batch < 4; batch += 1) { + for (let index = 0; index < 16_384; index += 1) { + append('') + } + expect(streams.flush()).toBe(true) + } + const originalJoin = Array.prototype.join + let retainedSlots = -1 + const spy = vi + .spyOn(Array.prototype, 'join') + .mockImplementation(function (this: unknown[], separator) { + // Byte counters cannot detect empty entries retained by the stream's chunk array. + if (separator === '' && this[0] === prefix) { + retainedSlots = this.length + } + return originalJoin.call(this, separator) + }) + let snapshot: ReturnType + try { + snapshot = streams.snapshot('thread', 'item') + } finally { + spy.mockRestore() + } + expect(retainedSlots).toBe(1) + expect(snapshot).toEqual({ + text: prefix, + observedBytes: Buffer.byteLength(prefix), + truncated: false + }) + append('é') + expect(streams.snapshot('thread', 'item')?.text).toBe(`${prefix}é`) + streams.forget('thread', 'item') + expect(streams.snapshot('thread', 'item')).toBeNull() + } finally { + streams.dispose() + } + }) + + it('preserves empty stream snapshots, scheduled publication and explicit flushes', () => { + const pending = new Set<() => void>() + const emitted: { key: string; text: string }[] = [] + const instance = createAgentSessionDeltaCoalescer({ + schedule: (run) => { + pending.add(run) + return () => { + pending.delete(run) + } + }, + emit: (key, text) => emitted.push({ key, text }) + }) + try { + expect(instance.append('empty', '')).toBe(true) + expect(instance.snapshot('empty')).toEqual({ + text: '', + observedBytes: 0, + truncated: false + }) + expect(pending.size).toBe(1) + expect(emitted).toEqual([]) + expect(instance.flushAll()).toBe(true) + expect(pending.size).toBe(0) + expect(emitted).toEqual([{ key: 'empty', text: '' }]) + instance.append('empty', 'visible') + instance.append('empty', '') + expect(pending.size).toBe(1) + expect(instance.flush('empty')).toBe(true) + expect(emitted.at(-1)).toEqual({ key: 'empty', text: 'visible' }) + instance.append('empty', '') + expect(instance.flushAll()).toBe(true) + expect(emitted).toHaveLength(3) + expect(emitted.at(-1)).toEqual({ key: 'empty', text: 'visible' }) + } finally { + instance.dispose() + } + expect(pending.size).toBe(0) + }) + + it('still refuses a new empty stream while the oldest output is backpressured', () => { + let accepting = false + const emitted: [string, string][] = [] + const instance = createAgentSessionDeltaCoalescer({ + maxStreams: 1, + schedule: () => () => {}, + emit: (key, text) => { + if (!accepting) { + return false + } + emitted.push([key, text]) + return true + } + }) + try { + instance.append('first', 'preserved') + expect(instance.append('second', '')).toBe(false) + expect(instance.snapshot('second')).toBeNull() + expect(instance.snapshot('first')?.text).toBe('preserved') + accepting = true + expect(instance.append('second', '')).toBe(true) + expect(instance.snapshot('first')).toBeNull() + expect(instance.snapshot('second')?.text).toBe('') + expect(instance.flushAll()).toBe(true) + expect(emitted).toEqual([ + ['first', 'preserved'], + ['second', ''] + ]) + } finally { + instance.dispose() + } + }) +}) From ab331253a0a8df91d66a4d2d68955ec74db90ec8 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:12:22 -0700 Subject: [PATCH 057/168] fix: release canceled working-directory waiter references (#21144) * fix: release canceled working-directory waiter references * test: normalize working-directory proof patch --------- Co-authored-by: m4air --- .../README.md | 59 + .../before.config.mjs | 24 + .../electron-results.json | 1260 +++++++++++++++++ .../fix.patch | 68 + .../node-results.json | 1259 ++++++++++++++++ .../reproduce.cjs | 107 ++ .../scenario.cjs | 235 +++ .../source-versions.json | 81 ++ .../sources.cjs | 100 ++ .../validation.json | 57 + ...ing-directory-validation-retention.test.ts | 225 +++ .../providers/working-directory-validation.ts | 62 +- 12 files changed, 3525 insertions(+), 12 deletions(-) create mode 100644 docs/audits/working-directory-wait-retention/README.md create mode 100644 docs/audits/working-directory-wait-retention/before.config.mjs create mode 100644 docs/audits/working-directory-wait-retention/electron-results.json create mode 100644 docs/audits/working-directory-wait-retention/fix.patch create mode 100644 docs/audits/working-directory-wait-retention/node-results.json create mode 100644 docs/audits/working-directory-wait-retention/reproduce.cjs create mode 100644 docs/audits/working-directory-wait-retention/scenario.cjs create mode 100644 docs/audits/working-directory-wait-retention/source-versions.json create mode 100644 docs/audits/working-directory-wait-retention/sources.cjs create mode 100644 docs/audits/working-directory-wait-retention/validation.json create mode 100644 src/main/providers/working-directory-validation-retention.test.ts diff --git a/docs/audits/working-directory-wait-retention/README.md b/docs/audits/working-directory-wait-retention/README.md new file mode 100644 index 00000000000..a7a2512e41a --- /dev/null +++ b/docs/audits/working-directory-wait-retention/README.md @@ -0,0 +1,59 @@ +# Canceled cwd validation waiter lifetime + +Canceled working-directory checks retained their AbortSignals while the shared native filesystem check remained pending. The change releases those caller references immediately while preserving the underlying native operation, raw-promise identity and callback ordering. + +**A small promise reaction and empty holder still remain per canceled wait until native settlement.** JavaScript promise reactions cannot be removed. This correction releases the signal, listener and caller resolvers; it does not establish a total bound on waiting metadata or explain an incident's memory magnitude. + +## Ownership and callers + +`src/main/providers/working-directory-validation.ts` keeps one pending validation per exact cwd. The raw `fs.stat` cannot be aborted, so the map entry and any UNC semaphore slot must survive caller cancellation until real settlement. Retiring them early would permit duplicate native work on the same stalled path. + +Previously each caller registered a `finally` callback that captured its signal. The fix keeps each caller's raw promise reaction in its original position, but that reaction now references a small holder. Abort or settlement removes the abort listener and clears the holder. A separate waiter factory prevents the first signal from sharing the map's cleanup closure. The redundant per-call rejection observer is removed; the existing map-level `then(forget, forget)` still handles native failure when every caller has left. + +The sole production importer is `pty-subprocess/spawn-preflight.ts:127–136`, through `local-pty-utils.ts`. `daemon-terminal-admission.ts` supplies a preparation signal, and `pty-subprocess.ts` forwards it to preflight. Ordinary daemon requests use a 30-second client timeout and a 5-second cancellation guard. A caller can therefore finish while the native filesystem operation remains pending across later requests. Those request timers do not bound the raw stat duration. + +No-signal callers still receive the exact original promise. Native map deletion, UNC lane ownership, WSL checks, creation reservations, shutdown and process authority are unchanged. The change stays on the execution host and applies to folder workspaces and git worktrees without a wire change. + +## Why each raw reaction remains + +Existing wait utilities were checked. A shared settlement observer changes this API's callback order: a raw-promise observer registered before a signal waiter can abort it before its raw result arrives. Moving every waiter behind an earlier shared observer would fulfill that waiter instead. The per-call holder preserves that order and synchronous cancellation. Six permanent regressions cover an external aborting observer before, between and after signal waiters, for native success and failure. + +## Before/after evidence + +The standalone proof bundles the actual validation module, UNC path parser and semaphore. Its native async stat is a deferred fixture; WSL subprocess operations throw if unexpectedly reached. It performs no actual cwd probe, remote filesystem access, native subprocess launch or app launch. The fixture contains 32 small canceled callers per outcome; no large payload is attached. + +| Before raw native settlement | Original Node 26.6.0 | Original Electron 43.7.0 / Node 24.21.0 | Fixed, both | +| ------------------------------- | -------------------- | --------------------------------------- | ----------- | +| Signals reachable | 32 | 32 | 0 | +| Cancellation errors reachable | 0 | 32 | 0 | +| Caller option objects reachable | 0 | 0 | 0 | +| Native stat calls | 1 | 1 | 1 | + +All measured caller objects collect after native settlement in both versions. Both native success and failure have the same lifetime result. Other controls pass on both runtimes: + +- Later live waiters receive the original operation's success or actionable error, and their listeners are removed. +- An already-aborted first caller still leaves the raw operation owned; no-signal callers share the same raw promise. +- Three canceled callers on one UNC host leave two native slots occupied. The third native operation starts only after one real completion. +- Forty-eight actual-module settlement/abort schedules and six raw-observer ordering cases match the original. +- Native rejection after all callers cancel produces no unhandled rejection. +- Synthetic CRLF source and patch reads produce identical reversed/fixed source and hashes without product writes. + +`sources.cjs` reverses `fix.patch` in memory and checks exact baseline and fixed SHA-256 values. Source hashes use canonical LF; reports include effective dependency and bundle hashes. No git history, ignored notes or copied production implementation is needed to rerun the proof. Each run has a 20-second deadline; the commands below set a 192 MiB heap limit. + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/working-directory-wait-retention/reproduce.cjs +``` + +For Electron, run its installed executable with the same flags and script path, setting `ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1`. It runs in Node mode and creates no windows. + +## Source compatibility and validation + +The audited baseline module is byte-identical to main commit `291b4ddd6f1c1af480169885e0fda7f9c78ff053` and release `v1.4.198` (`e0826956fcfc532f5a1e55b5e081f2e57e553c43`). All nine recorded caller/dependency sources also match that main commit. This one-product-file change does not depend on the shared waiter helper or its auth-wait changes. `source-versions.json` records the exact comparisons; historical source equality is not a historical packaged-runtime reproduction. + +The fixed three-file regression run passed 31 tests. Reversing only this fix gives one expected first-caller retention failure and 30 passing controls, including the six raw-observer cases: + +```sh +ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/working-directory-wait-retention/before.config.mjs src/main/providers/working-directory-validation-retention.test.ts src/main/providers/working-directory-validation.test.ts src/main/daemon/pty-subprocess-cwd-cancel-identity.test.ts +``` + +`validation.json` records verification results. The measured retention requires a still-pending native operation; no affected-host capture, byte slope or attribution to #19831 is claimed. diff --git a/docs/audits/working-directory-wait-retention/before.config.mjs b/docs/audits/working-directory-wait-retention/before.config.mjs new file mode 100644 index 00000000000..888f37f0890 --- /dev/null +++ b/docs/audits/working-directory-wait-retention/before.config.mjs @@ -0,0 +1,24 @@ +import { resolve } from 'node:path' +import { createRequire } from 'node:module' +import { defineConfig, mergeConfig } from 'vitest/config' +import baseConfig from '../../../config/vitest.config.ts' + +const { loadSources } = createRequire(import.meta.url)( + resolve('docs/audits/working-directory-wait-retention/sources.cjs') +) +const { before } = loadSources() +export default mergeConfig( + baseConfig, + defineConfig({ + plugins: [ + { + name: 'cwd-wait-before-fix', + enforce: 'pre', + transform(_code, id) { + const source = before.get(resolve(id.split('?')[0])) + return source === undefined ? undefined : { code: source, map: null } + } + } + ] + }) +) diff --git a/docs/audits/working-directory-wait-retention/electron-results.json b/docs/audits/working-directory-wait-retention/electron-results.json new file mode 100644 index 00000000000..24f1584f703 --- /dev/null +++ b/docs/audits/working-directory-wait-retention/electron-results.json @@ -0,0 +1,1260 @@ +{ + "runtime": { + "node": "24.21.0", + "acorn": "8.18.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.2", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "148", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "0.0.0", + "simdjson": "4.6.7", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2025c", + "undici": "7.29.1", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "15.0.245.31-electron.0", + "zlib": "1.3.2.1-motley", + "zstd": "1.6.0", + "electron": "43.7.0", + "chrome": "150.0.7871.250" + }, + "sourceHashLineEndings": "canonical LF", + "crlfLoaderControl": { + "syntheticCrlfReads": 2, + "identicalSourcesAndHashes": true + }, + "unhandledRejections": 0, + "reports": { + "before": { + "fulfilled": { + "beforeSettlement": { + "signal": 32, + "options": 0, + "error": 32 + }, + "afterSettlement": { + "signal": 0, + "options": 0, + "error": 0 + }, + "nativeCallsBeforeSettlement": 1, + "nativeCallsAfterFreshValidation": 2, + "lateResult": { + "status": "fulfilled" + } + }, + "rejected": { + "beforeSettlement": { + "signal": 32, + "options": 0, + "error": 32 + }, + "afterSettlement": { + "signal": 0, + "options": 0, + "error": 0 + }, + "nativeCallsBeforeSettlement": 1, + "nativeCallsAfterFreshValidation": 2, + "lateResult": { + "status": "rejected", + "message": "Working directory \"synthetic-validation-true\" does not exist. It may have been deleted or is on an unmounted volume (cwd: synthetic-validation-true, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + } + }, + "laneOwnership": { + "canceledWaits": 3, + "nativeCallsWhileBothSlotsOwned": 2, + "nativeCallsAfterOneRawCompletion": 3 + }, + "alreadyAborted": { + "nativeCalls": 1, + "noSignalPromiseIdentityPreserved": true + }, + "lateNativeRejection": { + "nativeRejectedAfterCallerCanceled": true + } + }, + "after": { + "fulfilled": { + "beforeSettlement": { + "signal": 0, + "options": 0, + "error": 0 + }, + "afterSettlement": { + "signal": 0, + "options": 0, + "error": 0 + }, + "nativeCallsBeforeSettlement": 1, + "nativeCallsAfterFreshValidation": 2, + "lateResult": { + "status": "fulfilled" + } + }, + "rejected": { + "beforeSettlement": { + "signal": 0, + "options": 0, + "error": 0 + }, + "afterSettlement": { + "signal": 0, + "options": 0, + "error": 0 + }, + "nativeCallsBeforeSettlement": 1, + "nativeCallsAfterFreshValidation": 2, + "lateResult": { + "status": "rejected", + "message": "Working directory \"synthetic-validation-true\" does not exist. It may have been deleted or is on an unmounted volume (cwd: synthetic-validation-true, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + } + }, + "laneOwnership": { + "canceledWaits": 3, + "nativeCallsWhileBothSlotsOwned": 2, + "nativeCallsAfterOneRawCompletion": 3 + }, + "alreadyAborted": { + "nativeCalls": 1, + "noSignalPromiseIdentityPreserved": true + }, + "lateNativeRejection": { + "nativeRejectedAfterCallerCanceled": true + } + } + }, + "matrix": [ + { + "reject": false, + "startedBefore": false, + "ticks": 0, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-0-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-0-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 0, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-0-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-0-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 1, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-1-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-1-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 1, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-1-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-1-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 2, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-2-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-2-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 2, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-2-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-2-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 3, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-3-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-3-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 3, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-3-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-3-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 4, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-4-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-4-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 4, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-4-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-4-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 8, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-8-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-8-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 8, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-8-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-8-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 0, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-0-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-0-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 0, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-0-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-0-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 1, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-1-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-1-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 1, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-1-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-1-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 2, + "abortFirst": false, + "before": { + "outcome": ["fulfilled"], + "calls": 1 + }, + "after": { + "outcome": ["fulfilled"], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 2, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-2-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-2-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 3, + "abortFirst": false, + "before": { + "outcome": ["fulfilled"], + "calls": 1 + }, + "after": { + "outcome": ["fulfilled"], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 3, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-3-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-3-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 4, + "abortFirst": false, + "before": { + "outcome": ["fulfilled"], + "calls": 1 + }, + "after": { + "outcome": ["fulfilled"], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 4, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-4-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-4-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 8, + "abortFirst": false, + "before": { + "outcome": ["fulfilled"], + "calls": 1 + }, + "after": { + "outcome": ["fulfilled"], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 8, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-8-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-8-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 0, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-0-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-0-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 0, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-0-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-0-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 1, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-1-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-1-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 1, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-1-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-1-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 2, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-2-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-2-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 2, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-2-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-2-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 3, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-3-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-3-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 3, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-3-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-3-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 4, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-4-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-4-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 4, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-4-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-4-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 8, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-8-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-8-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 8, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-8-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-8-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 0, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-0-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-0-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 0, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-0-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-0-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 1, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-1-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-1-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 1, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-1-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-1-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 2, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-2-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-2-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-2-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-2-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 2, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-2-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-2-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 3, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-3-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-3-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-3-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-3-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 3, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-3-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-3-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 4, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-4-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-4-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-4-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-4-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 4, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-4-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-4-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 8, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-8-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-8-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-8-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-8-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 8, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-8-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-8-true\" was canceled." + ], + "calls": 1 + } + } + ], + "observers": [ + { + "position": "before", + "reject": false, + "before": [["rejected", "WorkingDirectoryValidationAbortedError"]], + "after": [["rejected", "WorkingDirectoryValidationAbortedError"]] + }, + { + "position": "before", + "reject": true, + "before": [["rejected", "WorkingDirectoryValidationAbortedError"]], + "after": [["rejected", "WorkingDirectoryValidationAbortedError"]] + }, + { + "position": "between", + "reject": false, + "before": [["fulfilled"], ["rejected", "WorkingDirectoryValidationAbortedError"]], + "after": [["fulfilled"], ["rejected", "WorkingDirectoryValidationAbortedError"]] + }, + { + "position": "between", + "reject": true, + "before": [ + ["rejected", "Error"], + ["rejected", "WorkingDirectoryValidationAbortedError"] + ], + "after": [ + ["rejected", "Error"], + ["rejected", "WorkingDirectoryValidationAbortedError"] + ] + }, + { + "position": "after", + "reject": false, + "before": [["fulfilled"]], + "after": [["fulfilled"]] + }, + { + "position": "after", + "reject": true, + "before": [["rejected", "Error"]], + "after": [["rejected", "Error"]] + } + ], + "versions": { + "before": { + "sourceHashes": { + "src/main/providers/working-directory-validation.ts": { + "before": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "after": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212" + } + }, + "bundleSha256": "e4757e7593eccc8c48e69cc4a023983a020a25d0e31a90913d415a05030dff57", + "dependencies": [ + { + "path": "src/shared/wsl-paths.ts", + "sha256": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + }, + { + "path": "src/shared/priority-semaphore.ts", + "sha256": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5" + }, + { + "path": "src/main/providers/working-directory-validation.ts", + "sha256": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043" + } + ] + }, + "after": { + "sourceHashes": { + "src/main/providers/working-directory-validation.ts": { + "before": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "after": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212" + } + }, + "bundleSha256": "d9ddd8e54dcde6b6a33d529b8ba4a6f94318980e469398b81a3a991923dd95b1", + "dependencies": [ + { + "path": "src/shared/wsl-paths.ts", + "sha256": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + }, + { + "path": "src/shared/priority-semaphore.ts", + "sha256": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5" + }, + { + "path": "src/main/providers/working-directory-validation.ts", + "sha256": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212" + } + ] + } + }, + "scope": "Actual cwd validation and semaphore source. Native stat replaced by one bounded deferred fixture; no filesystem probe, WSL process or app. 32 canceled small signals per case. Native ownership retained until actual fixture settlement, including shared UNC lane. Small per-call reaction/empty-holder metadata still lives until native settlement. No synthetic large payload or incident RSS claim." +} diff --git a/docs/audits/working-directory-wait-retention/fix.patch b/docs/audits/working-directory-wait-retention/fix.patch new file mode 100644 index 00000000000..4b8dfc159f2 --- /dev/null +++ b/docs/audits/working-directory-wait-retention/fix.patch @@ -0,0 +1,68 @@ +diff --git a/src/main/providers/working-directory-validation.ts b/src/main/providers/working-directory-validation.ts +index 688cc85244..cf098cad2c 100644 +--- a/src/main/providers/working-directory-validation.ts ++++ b/src/main/providers/working-directory-validation.ts +@@ -165,12 +165 @@ export function validateWorkingDirectoryAsync( +- const shared = validation +- // The shared probe outlives this caller; keep it from surfacing as unhandled. +- void shared.catch(() => {}) +- return new Promise((resolve, reject) => { +- const onAbort = (): void => reject(new WorkingDirectoryValidationAbortedError(cwd)) +- if (signal.aborted) { +- onAbort() +- return +- } +- signal.addEventListener('abort', onAbort, { once: true }) +- shared.then(resolve, reject).finally(() => signal.removeEventListener('abort', onAbort)) +- }) ++ return waitForWorkingDirectoryValidation(validation, cwd, signal) +@@ -204,0 +194,49 @@ async function probeWorkingDirectory(cwd: string): Promise { ++ ++type WorkingDirectoryWaiterHolder = { ++ waiter: { ++ signal: AbortSignal ++ onAbort: () => void ++ resolve: () => void ++ reject: (error: unknown) => void ++ } | null ++} ++ ++function takeWorkingDirectoryWaiter( ++ holder: WorkingDirectoryWaiterHolder ++): WorkingDirectoryWaiterHolder['waiter'] { ++ const waiter = holder.waiter ++ holder.waiter = null ++ waiter?.signal.removeEventListener('abort', waiter.onAbort) ++ return waiter ++} ++ ++// Keep reaction order while an abandoned caller's signal and resolver become collectible. ++function observeWorkingDirectoryValidation( ++ promise: Promise, ++ holder: WorkingDirectoryWaiterHolder ++): void { ++ void promise.then( ++ () => takeWorkingDirectoryWaiter(holder)?.resolve(), ++ (error: unknown) => takeWorkingDirectoryWaiter(holder)?.reject(error) ++ ) ++} ++ ++function waitForWorkingDirectoryValidation( ++ shared: Promise, ++ cwd: string, ++ signal: AbortSignal ++): Promise { ++ return new Promise((resolve, reject) => { ++ const holder: WorkingDirectoryWaiterHolder = { waiter: null } ++ const onAbort = (): void => { ++ takeWorkingDirectoryWaiter(holder)?.reject(new WorkingDirectoryValidationAbortedError(cwd)) ++ } ++ holder.waiter = { signal, onAbort, resolve, reject } ++ if (signal.aborted) { ++ onAbort() ++ return ++ } ++ signal.addEventListener('abort', onAbort, { once: true }) ++ observeWorkingDirectoryValidation(shared, holder) ++ }) ++} diff --git a/docs/audits/working-directory-wait-retention/node-results.json b/docs/audits/working-directory-wait-retention/node-results.json new file mode 100644 index 00000000000..d2dc9087473 --- /dev/null +++ b/docs/audits/working-directory-wait-retention/node-results.json @@ -0,0 +1,1259 @@ +{ + "runtime": { + "node": "26.6.0", + "acorn": "8.17.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.3", + "libffi": "3.7.1", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "147", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "3.6.3", + "simdjson": "4.6.6", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2026a", + "undici": "8.9.0", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "14.6.202.34-node.26", + "zlib": "1.2.12", + "zstd": "1.5.7" + }, + "sourceHashLineEndings": "canonical LF", + "crlfLoaderControl": { + "syntheticCrlfReads": 2, + "identicalSourcesAndHashes": true + }, + "unhandledRejections": 0, + "reports": { + "before": { + "fulfilled": { + "beforeSettlement": { + "signal": 32, + "options": 0, + "error": 0 + }, + "afterSettlement": { + "signal": 0, + "options": 0, + "error": 0 + }, + "nativeCallsBeforeSettlement": 1, + "nativeCallsAfterFreshValidation": 2, + "lateResult": { + "status": "fulfilled" + } + }, + "rejected": { + "beforeSettlement": { + "signal": 32, + "options": 0, + "error": 0 + }, + "afterSettlement": { + "signal": 0, + "options": 0, + "error": 0 + }, + "nativeCallsBeforeSettlement": 1, + "nativeCallsAfterFreshValidation": 2, + "lateResult": { + "status": "rejected", + "message": "Working directory \"synthetic-validation-true\" does not exist. It may have been deleted or is on an unmounted volume (cwd: synthetic-validation-true, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + } + }, + "laneOwnership": { + "canceledWaits": 3, + "nativeCallsWhileBothSlotsOwned": 2, + "nativeCallsAfterOneRawCompletion": 3 + }, + "alreadyAborted": { + "nativeCalls": 1, + "noSignalPromiseIdentityPreserved": true + }, + "lateNativeRejection": { + "nativeRejectedAfterCallerCanceled": true + } + }, + "after": { + "fulfilled": { + "beforeSettlement": { + "signal": 0, + "options": 0, + "error": 0 + }, + "afterSettlement": { + "signal": 0, + "options": 0, + "error": 0 + }, + "nativeCallsBeforeSettlement": 1, + "nativeCallsAfterFreshValidation": 2, + "lateResult": { + "status": "fulfilled" + } + }, + "rejected": { + "beforeSettlement": { + "signal": 0, + "options": 0, + "error": 0 + }, + "afterSettlement": { + "signal": 0, + "options": 0, + "error": 0 + }, + "nativeCallsBeforeSettlement": 1, + "nativeCallsAfterFreshValidation": 2, + "lateResult": { + "status": "rejected", + "message": "Working directory \"synthetic-validation-true\" does not exist. It may have been deleted or is on an unmounted volume (cwd: synthetic-validation-true, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + } + }, + "laneOwnership": { + "canceledWaits": 3, + "nativeCallsWhileBothSlotsOwned": 2, + "nativeCallsAfterOneRawCompletion": 3 + }, + "alreadyAborted": { + "nativeCalls": 1, + "noSignalPromiseIdentityPreserved": true + }, + "lateNativeRejection": { + "nativeRejectedAfterCallerCanceled": true + } + } + }, + "matrix": [ + { + "reject": false, + "startedBefore": false, + "ticks": 0, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-0-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-0-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 0, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-0-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-0-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 1, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-1-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-1-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 1, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-1-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-1-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 2, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-2-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-2-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 2, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-2-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-2-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 3, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-3-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-3-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 3, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-3-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-3-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 4, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-4-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-4-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 4, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-4-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-4-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 8, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-8-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-8-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": false, + "startedBefore": false, + "ticks": 8, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-8-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-false-8-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 0, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-0-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-0-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 0, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-0-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-0-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 1, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-1-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-1-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 1, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-1-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-1-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 2, + "abortFirst": false, + "before": { + "outcome": ["fulfilled"], + "calls": 1 + }, + "after": { + "outcome": ["fulfilled"], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 2, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-2-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-2-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 3, + "abortFirst": false, + "before": { + "outcome": ["fulfilled"], + "calls": 1 + }, + "after": { + "outcome": ["fulfilled"], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 3, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-3-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-3-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 4, + "abortFirst": false, + "before": { + "outcome": ["fulfilled"], + "calls": 1 + }, + "after": { + "outcome": ["fulfilled"], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 4, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-4-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-4-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 8, + "abortFirst": false, + "before": { + "outcome": ["fulfilled"], + "calls": 1 + }, + "after": { + "outcome": ["fulfilled"], + "calls": 1 + } + }, + { + "reject": false, + "startedBefore": true, + "ticks": 8, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-8-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-false-true-8-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 0, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-0-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-0-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 0, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-0-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-0-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 1, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-1-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-1-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 1, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-1-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-1-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 2, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-2-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-2-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 2, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-2-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-2-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 3, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-3-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-3-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 3, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-3-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-3-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 4, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-4-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-4-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 4, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-4-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-4-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 8, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-8-false\" was canceled." + ], + "calls": 2 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-8-false\" was canceled." + ], + "calls": 2 + } + }, + { + "reject": true, + "startedBefore": false, + "ticks": 8, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-8-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-false-8-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 0, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-0-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-0-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 0, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-0-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-0-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 1, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-1-false\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-1-false\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 1, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-1-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-1-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 2, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-2-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-2-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-2-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-2-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 2, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-2-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-2-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 3, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-3-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-3-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-3-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-3-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 3, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-3-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-3-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 4, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-4-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-4-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-4-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-4-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 4, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-4-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-4-true\" was canceled." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 8, + "abortFirst": false, + "before": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-8-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-8-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "Error", + "Working directory \"matrix-true-true-8-false\" does not exist. It may have been deleted or is on an unmounted volume (cwd: matrix-true-true-8-false, arch: arm64, platform: darwin 25.6.0, orca: synthetic-cwd-validation-audit)." + ], + "calls": 1 + } + }, + { + "reject": true, + "startedBefore": true, + "ticks": 8, + "abortFirst": true, + "before": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-8-true\" was canceled." + ], + "calls": 1 + }, + "after": { + "outcome": [ + "rejected", + "WorkingDirectoryValidationAbortedError", + "Working directory validation for \"matrix-true-true-8-true\" was canceled." + ], + "calls": 1 + } + } + ], + "observers": [ + { + "position": "before", + "reject": false, + "before": [["rejected", "WorkingDirectoryValidationAbortedError"]], + "after": [["rejected", "WorkingDirectoryValidationAbortedError"]] + }, + { + "position": "before", + "reject": true, + "before": [["rejected", "WorkingDirectoryValidationAbortedError"]], + "after": [["rejected", "WorkingDirectoryValidationAbortedError"]] + }, + { + "position": "between", + "reject": false, + "before": [["fulfilled"], ["rejected", "WorkingDirectoryValidationAbortedError"]], + "after": [["fulfilled"], ["rejected", "WorkingDirectoryValidationAbortedError"]] + }, + { + "position": "between", + "reject": true, + "before": [ + ["rejected", "Error"], + ["rejected", "WorkingDirectoryValidationAbortedError"] + ], + "after": [ + ["rejected", "Error"], + ["rejected", "WorkingDirectoryValidationAbortedError"] + ] + }, + { + "position": "after", + "reject": false, + "before": [["fulfilled"]], + "after": [["fulfilled"]] + }, + { + "position": "after", + "reject": true, + "before": [["rejected", "Error"]], + "after": [["rejected", "Error"]] + } + ], + "versions": { + "before": { + "sourceHashes": { + "src/main/providers/working-directory-validation.ts": { + "before": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "after": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212" + } + }, + "bundleSha256": "e4757e7593eccc8c48e69cc4a023983a020a25d0e31a90913d415a05030dff57", + "dependencies": [ + { + "path": "src/shared/wsl-paths.ts", + "sha256": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + }, + { + "path": "src/shared/priority-semaphore.ts", + "sha256": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5" + }, + { + "path": "src/main/providers/working-directory-validation.ts", + "sha256": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043" + } + ] + }, + "after": { + "sourceHashes": { + "src/main/providers/working-directory-validation.ts": { + "before": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "after": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212" + } + }, + "bundleSha256": "d9ddd8e54dcde6b6a33d529b8ba4a6f94318980e469398b81a3a991923dd95b1", + "dependencies": [ + { + "path": "src/shared/wsl-paths.ts", + "sha256": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + }, + { + "path": "src/shared/priority-semaphore.ts", + "sha256": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5" + }, + { + "path": "src/main/providers/working-directory-validation.ts", + "sha256": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212" + } + ] + } + }, + "scope": "Actual cwd validation and semaphore source. Native stat replaced by one bounded deferred fixture; no filesystem probe, WSL process or app. 32 canceled small signals per case. Native ownership retained until actual fixture settlement, including shared UNC lane. Small per-call reaction/empty-holder metadata still lives until native settlement. No synthetic large payload or incident RSS claim." +} diff --git a/docs/audits/working-directory-wait-retention/reproduce.cjs b/docs/audits/working-directory-wait-retention/reproduce.cjs new file mode 100644 index 00000000000..66a413f5ddb --- /dev/null +++ b/docs/audits/working-directory-wait-retention/reproduce.cjs @@ -0,0 +1,107 @@ +const assert = require('node:assert/strict') +const { readFileSync, writeFileSync } = require('node:fs') +const path = require('node:path') +const { load, loadSources, canonicalLf } = require('./sources.cjs') +const { + fixtureKey, + lifetime, + laneOwnership, + ordering, + observerOrdering, + alreadyAborted, + canceledNativeRejection +} = require('./scenario.cjs') + +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function') +process.env.ORCA_APP_VERSION = 'synthetic-cwd-validation-audit' +function checkCrlfLoader() { + const baseline = loadSources() + let syntheticCrlfReads = 0 + const crlf = loadSources({ + readText(file) { + syntheticCrlfReads++ + return canonicalLf(readFileSync(file, 'utf8')).replace(/\n/g, '\r\n') + } + }) + assert.equal(syntheticCrlfReads, 2) + assert.deepEqual(crlf.before, baseline.before) + assert.deepEqual(crlf.after, baseline.after) + assert.deepEqual(crlf.hashes, baseline.hashes) + return { syntheticCrlfReads, identicalSourcesAndHashes: true } +} + +async function main() { + const timer = setTimeout(() => { + process.stderr.write('deadline\n') + process.exit(2) + }, 20_000) + const unhandled = [] + const recordUnhandled = (error) => unhandled.push(error) + process.on('unhandledRejection', recordUnhandled) + const crlfLoaderControl = checkCrlfLoader() + const loaded = { before: await load(false, fixtureKey), after: await load(true, fixtureKey) } + const reports = {} + for (const [mode, validation] of Object.entries(loaded)) { + reports[mode] = { + fulfilled: await lifetime(validation, mode === 'after', false), + rejected: await lifetime(validation, mode === 'after', true), + laneOwnership: await laneOwnership(validation), + alreadyAborted: await alreadyAborted(validation), + lateNativeRejection: await canceledNativeRejection(validation) + } + } + const matrix = [] + for (const reject of [false, true]) { + for (const startedBefore of [false, true]) { + for (const ticks of [0, 1, 2, 3, 4, 8]) { + for (const abortFirst of [false, true]) { + const args = [reject, startedBefore, ticks, abortFirst] + const before = await ordering(loaded.before, ...args) + const after = await ordering(loaded.after, ...args) + assert.deepEqual(after, before) + matrix.push({ reject, startedBefore, ticks, abortFirst, before, after }) + } + } + } + } + const observers = [] + for (const position of ['before', 'between', 'after']) { + for (const reject of [false, true]) { + const before = await observerOrdering(loaded.before, position, reject) + const after = await observerOrdering(loaded.after, position, reject) + assert.deepEqual(after, before) + observers.push({ position, reject, before, after }) + } + } + await new Promise(setImmediate) + assert.deepEqual(unhandled, []) + process.off('unhandledRejection', recordUnhandled) + clearTimeout(timer) + delete globalThis[fixtureKey] + const report = { + runtime: process.versions, + sourceHashLineEndings: 'canonical LF', + crlfLoaderControl, + unhandledRejections: unhandled.length, + reports, + matrix, + observers, + versions: Object.fromEntries( + Object.entries(loaded).map(([mode, value]) => [mode, value.versions]) + ), + scope: + 'Actual cwd validation and semaphore source. Native stat replaced by one bounded deferred fixture; no filesystem probe, WSL process or app. 32 canceled small signals per case. Native ownership retained until actual fixture settlement, including shared UNC lane. Small per-call reaction/empty-holder metadata still lives until native settlement. No synthetic large payload or incident RSS claim.' + } + writeFileSync( + path.join(__dirname, process.versions.electron ? 'electron-results.json' : 'node-results.json'), + `${JSON.stringify(report, null, 2)}\n` + ) + process.stdout.write( + `${JSON.stringify({ runtime: process.versions.node, reports, orderingCases: matrix.length, observerCases: observers.length, unhandledRejections: unhandled.length }, null, 2)}\n` + ) +} +main().catch((error) => { + process.stderr.write(`${error.stack}\n`) + process.exit(1) +}) diff --git a/docs/audits/working-directory-wait-retention/scenario.cjs b/docs/audits/working-directory-wait-retention/scenario.cjs new file mode 100644 index 00000000000..3e72892ee91 --- /dev/null +++ b/docs/audits/working-directory-wait-retention/scenario.cjs @@ -0,0 +1,235 @@ +const assert = require('node:assert/strict') +const { getEventListeners } = require('node:events') + +const fixtureKey = '__orcaWorkingDirectoryWaitFixture' +const validDirectory = { isDirectory: () => true } +async function collect() { + for (let round = 0; round < 6; round++) { + await new Promise(setImmediate) + global.gc() + } +} +const tick = async (count) => { + for (let i = 0; i < count; i++) { + await Promise.resolve() + } +} + +async function canceledWait(validation, cwd) { + const controller = new AbortController() + const options = { signal: controller.signal } + const refs = { signal: new WeakRef(controller.signal), options: new WeakRef(options) } + const waiting = validation.validateWorkingDirectoryAsync(cwd, options) + controller.abort() + await assert.rejects(waiting, (error) => { + refs.error = new WeakRef(error) + return error instanceof validation.WorkingDirectoryValidationAbortedError + }) + assert.equal(getEventListeners(controller.signal, 'abort').length, 0) + return refs +} +const counts = (refs) => + Object.fromEntries( + ['signal', 'options', 'error'].map((key) => [ + key, + refs.filter((ref) => ref[key].deref()).length + ]) + ) + +async function lifetime(validation, fixed, reject) { + const gate = Promise.withResolvers() + let statCalls = 0 + globalThis[fixtureKey] = { + stat() { + statCalls++ + return gate.promise + } + } + const cwd = `synthetic-validation-${reject}` + const refs = [] + for (let index = 0; index < 32; index++) { + refs.push(await canceledWait(validation, cwd)) + } + await collect() + const beforeSettlement = counts(refs) + assert.equal(statCalls, 1) + assert.equal(beforeSettlement.options, 0) + assert.equal(beforeSettlement.signal, fixed ? 0 : 32) + if (fixed) { + assert.equal(beforeSettlement.error, 0) + } + const controller = new AbortController() + const late = validation.validateWorkingDirectoryAsync(cwd, { signal: controller.signal }).then( + () => ({ status: 'fulfilled' }), + (error) => ({ status: 'rejected', message: error.message }) + ) + assert.equal(statCalls, 1) + assert.equal(getEventListeners(controller.signal, 'abort').length, 1) + if (reject) { + gate.reject(new Error('synthetic native failure')) + } else { + gate.resolve(validDirectory) + } + const lateResult = await late + assert.equal(lateResult.status, reject ? 'rejected' : 'fulfilled') + await collect() + const afterSettlement = counts(refs) + assert.deepEqual(afterSettlement, { signal: 0, options: 0, error: 0 }) + assert.equal(getEventListeners(controller.signal, 'abort').length, 0) + globalThis[fixtureKey] = { + stat() { + statCalls++ + return Promise.resolve(validDirectory) + } + } + await validation.validateWorkingDirectoryAsync(cwd) + assert.equal(statCalls, 2) + return { + beforeSettlement, + afterSettlement, + nativeCallsBeforeSettlement: 1, + nativeCallsAfterFreshValidation: statCalls, + lateResult + } +} + +async function laneOwnership(validation) { + const gates = [Promise.withResolvers(), Promise.withResolvers(), Promise.withResolvers()] + const started = [] + globalThis[fixtureKey] = { + stat(cwd) { + started.push(cwd) + return gates[started.length - 1].promise + } + } + const paths = Array.from({ length: 3 }, (_, index) => `\\\\synthetic-host\\dir-${index}`) + for (const cwd of paths) { + await canceledWait(validation, cwd) + } + await tick(8) + assert.equal(started.length, 2) + gates[0].resolve(validDirectory) + await new Promise(setImmediate) + assert.equal(started.length, 3) + gates[1].resolve(validDirectory) + gates[2].resolve(validDirectory) + await new Promise(setImmediate) + return { + canceledWaits: 3, + nativeCallsWhileBothSlotsOwned: 2, + nativeCallsAfterOneRawCompletion: 3 + } +} + +async function ordering(validation, reject, startedBefore, ticks, abortFirst) { + const gate = Promise.withResolvers() + let calls = 0 + globalThis[fixtureKey] = { + stat() { + calls++ + return calls === 1 ? gate.promise : Promise.resolve(validDirectory) + } + } + const cwd = `matrix-${reject}-${startedBefore}-${ticks}-${abortFirst}` + const anchor = validation.validateWorkingDirectoryAsync(cwd).catch(() => {}) + const controller = new AbortController() + const start = () => + validation.validateWorkingDirectoryAsync(cwd, { signal: controller.signal }).then( + () => ['fulfilled'], + (error) => ['rejected', error.name, error.message] + ) + let waiting = startedBefore ? start() : null + const settle = () => + reject ? gate.reject(new Error('raw failure')) : gate.resolve(validDirectory) + if (abortFirst) { + controller.abort() + } else { + settle() + } + await tick(ticks) + waiting ??= start() + if (abortFirst) { + settle() + } else { + controller.abort() + } + const outcome = await waiting + await anchor + await new Promise(setImmediate) + assert.equal(getEventListeners(controller.signal, 'abort').length, 0) + return { outcome, calls } +} + +async function observerOrdering(validation, position, reject) { + const gate = Promise.withResolvers() + globalThis[fixtureKey] = { stat: () => gate.promise } + const cwd = `observer-${position}-${reject}` + const raw = validation.validateWorkingDirectoryAsync(cwd) + const controller = new AbortController() + const start = () => + validation.validateWorkingDirectoryAsync(cwd, { signal: controller.signal }).then( + () => ['fulfilled'], + (error) => ['rejected', error.name] + ) + const waiting = [] + if (position !== 'before') { + waiting.push(start()) + } + const abortObserver = raw.then( + () => controller.abort(), + () => controller.abort() + ) + if (position !== 'after') { + waiting.push(start()) + } + if (reject) { + gate.reject(new Error('raw failure')) + } else { + gate.resolve(validDirectory) + } + const outcomes = await Promise.all(waiting) + await abortObserver + return outcomes +} + +async function alreadyAborted(validation) { + const gate = Promise.withResolvers() + let nativeCalls = 0 + globalThis[fixtureKey] = { + stat() { + nativeCalls++ + return gate.promise + } + } + const signal = AbortSignal.abort() + await assert.rejects( + validation.validateWorkingDirectoryAsync('pre-aborted', { signal }), + (error) => error instanceof validation.WorkingDirectoryValidationAbortedError + ) + assert.equal(getEventListeners(signal, 'abort').length, 0) + const raw = validation.validateWorkingDirectoryAsync('pre-aborted') + assert.equal(validation.validateWorkingDirectoryAsync('pre-aborted'), raw) + assert.equal(nativeCalls, 1) + gate.resolve(validDirectory) + await raw + return { nativeCalls, noSignalPromiseIdentityPreserved: true } +} + +async function canceledNativeRejection(validation) { + const gate = Promise.withResolvers() + globalThis[fixtureKey] = { stat: () => gate.promise } + await canceledWait(validation, 'late-native-rejection') + gate.reject(new Error('Native failure after all callers canceled')) + await new Promise(setImmediate) + return { nativeRejectedAfterCallerCanceled: true } +} + +module.exports = { + fixtureKey, + lifetime, + laneOwnership, + ordering, + observerOrdering, + alreadyAborted, + canceledNativeRejection +} diff --git a/docs/audits/working-directory-wait-retention/source-versions.json b/docs/audits/working-directory-wait-retention/source-versions.json new file mode 100644 index 00000000000..fbb478d2c02 --- /dev/null +++ b/docs/audits/working-directory-wait-retention/source-versions.json @@ -0,0 +1,81 @@ +{ + "baselineHashes": { + "src/main/providers/working-directory-validation.ts": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043" + }, + "fixedHashes": { + "src/main/providers/working-directory-validation.ts": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212" + }, + "namedRefs": [ + { + "ref": "HEAD", + "revision": "96970f9b6efe915f9578b765c93f3d06880ccc2d", + "sha256": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "matchesAuditBaseline": true, + "patchAppliesWithIdenticalFixedHash": true + }, + { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "revision": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "matchesAuditBaseline": true, + "patchAppliesWithIdenticalFixedHash": true + }, + { + "ref": "v1.4.198", + "revision": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "matchesAuditBaseline": true, + "patchAppliesWithIdenticalFixedHash": true + } + ], + "sourceHashLineEndings": "canonical LF", + "historicalRuntimeReproduced": false, + "sharedWaiterDependency": false, + "callerProvenance": [ + { + "path": "src/main/daemon/pty-subprocess/spawn-preflight.ts", + "sha256": "54b19c44a2f7beabb7a1dcb817efc26be1c9b13462a410e82c2da88f5550ceb2", + "main291bSha256": "54b19c44a2f7beabb7a1dcb817efc26be1c9b13462a410e82c2da88f5550ceb2" + }, + { + "path": "src/main/providers/local-pty-utils.ts", + "sha256": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "main291bSha256": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25" + }, + { + "path": "src/main/daemon/pty-subprocess.ts", + "sha256": "9f32c99c79fd3a23fd61d572146f81c3680fb5cbc13337e4fb3179856a0d7dc1", + "main291bSha256": "9f32c99c79fd3a23fd61d572146f81c3680fb5cbc13337e4fb3179856a0d7dc1" + }, + { + "path": "src/main/daemon/daemon-terminal-admission.ts", + "sha256": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "main291bSha256": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251" + }, + { + "path": "src/main/daemon/daemon-pty-spawn-preparations.ts", + "sha256": "77c94466578a5066af069e45eb5e87a64b73b88580a13b2172b2ea846b434f4e", + "main291bSha256": "77c94466578a5066af069e45eb5e87a64b73b88580a13b2172b2ea846b434f4e" + }, + { + "path": "src/main/daemon/daemon-client-rpc-request.ts", + "sha256": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "main291bSha256": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b" + }, + { + "path": "src/main/daemon/client.ts", + "sha256": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "main291bSha256": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14" + }, + { + "path": "src/shared/priority-semaphore.ts", + "sha256": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "main291bSha256": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5" + }, + { + "path": "src/shared/wsl-paths.ts", + "sha256": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "main291bSha256": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a" + } + ] +} diff --git a/docs/audits/working-directory-wait-retention/sources.cjs b/docs/audits/working-directory-wait-retention/sources.cjs new file mode 100644 index 00000000000..c42f415cb05 --- /dev/null +++ b/docs/audits/working-directory-wait-retention/sources.cjs @@ -0,0 +1,100 @@ +const assert = require('node:assert/strict') +const { readFileSync } = require('node:fs') +const { createHash } = require('node:crypto') +const path = require('node:path') +const Module = require('node:module') +const esbuild = require('esbuild') +const root = path.resolve(__dirname, '../../..') +const read = (file) => readFileSync(file, 'utf8').replace(/\r\n/g, '\n') +const sha = (value) => createHash('sha256').update(value).digest('hex') +const sourcePath = 'src/main/providers/working-directory-validation.ts' +const { applyPatch, parsePatch, reversePatch } = require('diff') +const { resolve } = path +const canonicalLf = (text) => text.replace(/\r\n/g, '\n') + +function loadSources({ readText = (file) => readFileSync(file, 'utf8') } = {}) { + const root = resolve(__dirname, '../../..') + const expected = JSON.parse(readFileSync(resolve(__dirname, 'source-versions.json'), 'utf8')) + const parsed = parsePatch(canonicalLf(readText(resolve(__dirname, 'fix.patch')))) + const before = new Map() + const after = new Map() + const hashes = {} + assert.equal(parsed.length, 1) + for (const patch of parsed) { + const path = patch.newFileName.replace(/^b\//, '') + assert.ok(Object.hasOwn(expected.baselineHashes, path), `Unexpected patch path: ${path}`) + const absolute = resolve(root, path) + const current = canonicalLf(readText(absolute)) + const baseline = applyPatch(current, reversePatch(patch)) + assert.notEqual(baseline, false, `Source changed; review fix.patch: ${path}`) + const hash = (source) => createHash('sha256').update(source).digest('hex') + assert.equal(hash(baseline), expected.baselineHashes[path], `Baseline drift: ${path}`) + assert.equal(hash(current), expected.fixedHashes[path], `Fixed source drift: ${path}`) + before.set(absolute, baseline) + after.set(absolute, current) + hashes[path] = { before: hash(baseline), after: hash(current) } + } + return { root, before, after, hashes } +} + +async function load(fixed, fixtureKey) { + const { before, after, hashes } = loadSources() + const original = before.get(path.join(root, sourcePath)) + const candidate = after.get(path.join(root, sourcePath)) + const build = await esbuild.build({ + entryPoints: [path.join(root, sourcePath)], + platform: 'node', + format: 'cjs', + bundle: true, + packages: 'external', + write: false, + metafile: true, + plugins: [ + { + name: 'validation-native-stat-port', + setup(builder) { + builder.onLoad({ filter: /working-directory-validation\.ts$/ }, (args) => { + assert.equal(args.path, path.join(root, sourcePath)) + return { contents: fixed ? candidate : original, loader: 'ts' } + }) + builder.onResolve({ filter: /^node:fs\/promises$/ }, () => ({ + path: 'native-stat', + namespace: 'fixture' + })) + builder.onResolve({ filter: /\/wsl$/ }, () => ({ + path: 'no-wsl-process', + namespace: 'fixture' + })) + builder.onLoad({ filter: /.*/, namespace: 'fixture' }, (args) => ({ + contents: + args.path === 'native-stat' + ? `export const stat = (...args) => globalThis[${JSON.stringify(fixtureKey)}].stat(...args)` + : "const unexpected = () => { throw new Error('No native WSL operation permitted') }; export const wslUncDirectoryExists = unexpected; export const wslUncDirectoryExistsAsync = unexpected", + loader: 'js' + })) + } + } + ] + }) + const filename = path.join(__dirname, 'in-memory-validation.cjs') + const loaded = new Module(filename, module) + loaded.filename = filename + loaded.paths = Module._nodeModulePaths(__dirname) + loaded._compile(build.outputFiles[0].text, filename) + return { + ...loaded.exports, + versions: { + sourceHashes: hashes, + bundleSha256: sha(build.outputFiles[0].contents), + dependencies: Object.keys(build.metafile.inputs) + .filter((file) => file.startsWith('src/')) + .map((file) => ({ + path: file, + sha256: sha( + file === sourcePath ? (fixed ? candidate : original) : read(path.join(root, file)) + ) + })) + } + } +} +module.exports = { load, loadSources, canonicalLf } diff --git a/docs/audits/working-directory-wait-retention/validation.json b/docs/audits/working-directory-wait-retention/validation.json new file mode 100644 index 00000000000..a08ebe46f00 --- /dev/null +++ b/docs/audits/working-directory-wait-retention/validation.json @@ -0,0 +1,57 @@ +{ + "tests": { + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/providers/working-directory-validation-retention.test.ts src/main/providers/working-directory-validation.test.ts src/main/daemon/pty-subprocess-cwd-cancel-identity.test.ts", + "passed": 31, + "files": 3, + "exitCode": 0, + "newRegressionCases": 12 + }, + "baselineOverlay": { + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/working-directory-wait-retention/before.config.mjs src/main/providers/working-directory-validation-retention.test.ts src/main/providers/working-directory-validation.test.ts src/main/daemon/pty-subprocess-cwd-cancel-identity.test.ts", + "passed": 30, + "expectedFailed": 1, + "failure": "releases the first caller and subsequent canceled callers while their native stat stays owned", + "exitCode": 1 + }, + "typecheck": { + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm tc:node", + "exitCode": 0 + }, + "lint": { + "files": [ + "src/main/providers/working-directory-validation.ts", + "src/main/providers/working-directory-validation-retention.test.ts", + "docs/audits/working-directory-wait-retention/sources.cjs", + "docs/audits/working-directory-wait-retention/scenario.cjs", + "docs/audits/working-directory-wait-retention/reproduce.cjs", + "docs/audits/working-directory-wait-retention/before.config.mjs" + ], + "ordinary": "pnpm exec oxlint --no-ignore ", + "typeAware": "pnpm exec oxlint --no-ignore --type-aware --config config/oxlint-code-quality-type-aware.json --deny-warnings", + "exitCodes": [0, 0] + }, + "changedQuality": { + "command": "ORCA_BACKGROUND_LAUNCH=1 ORCA_CODE_QUALITY_BASE=96970f9b6efe915f9578b765c93f3d06880ccc2d pnpm run check:code-quality:changed", + "exitCode": 0, + "changedFiles": 2, + "newFindings": 0 + }, + "proofs": { + "node": "ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/working-directory-wait-retention/reproduce.cjs", + "electron": "Installed Electron executable with ELECTRON_RUN_AS_NODE=1 and ORCA_BACKGROUND_LAUNCH=1; same flags and proof path.", + "exitCodes": [0, 0], + "nativeSettlementOrderingCasesPerRuntime": 48, + "externalRawObserverCasesPerRuntime": 6, + "unhandledRejectionsPerRuntime": 0, + "crlfLoaderControlPerRuntime": true + }, + "format": "All product TS and artifact CJS/MJS/MD/JSON checked with oxfmt --stdin-filepath; patch excluded.", + "gitDiffCheckExitCode": 0, + "historicalCompatibility": "All three named baseline refs accept fix.patch and produce identical fixed SHA-256; all nine recorded provenance sources match main291b. No shared-waiter dependency.", + "artifactNormalization": { + "change": "Regenerated fix.patch with zero context so stored context blank lines do not appear as trailing whitespace when checked as a new artifact. Product/tests unchanged.", + "proofs": "Both Node/Electron 54-case ordering/lifecycle runs pass with the regenerated patch; formatted result files are byte-identical to the prior capture.", + "quality": "All six published code files explicitly scanned through five quality configurations with --no-ignore. Ordinary/type-aware/React/design scans pass; whole-file casting scan reports one unchanged assertion at product line84 present in the baseline. Root changed-lines gate since96970f9b passes all five scans across11 changed files with zero new findings.", + "whitespace": "All 12 publication files checked as complete additions; no whitespace diagnostics." + } +} diff --git a/src/main/providers/working-directory-validation-retention.test.ts b/src/main/providers/working-directory-validation-retention.test.ts new file mode 100644 index 00000000000..ab711bdadf3 --- /dev/null +++ b/src/main/providers/working-directory-validation-retention.test.ts @@ -0,0 +1,225 @@ +import { getEventListeners } from 'node:events' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +const { stat } = vi.hoisted(() => ({ + stat: vi.fn<() => Promise<{ isDirectory: () => boolean }>>() +})) +vi.mock('node:fs/promises', () => ({ stat })) +vi.mock('../wsl', () => ({ + wslUncDirectoryExists: () => { + throw new Error('Unexpected WSL probe') + }, + wslUncDirectoryExistsAsync: () => { + throw new Error('Unexpected WSL probe') + } +})) + +import { + _resetWorkingDirectoryValidationStateForTest, + validateWorkingDirectoryAsync as validate, + WorkingDirectoryValidationAbortedError +} from './working-directory-validation' + +const directory = { isDirectory: () => true } +const cwd = 'synthetic-cwd-wait-retention' + +function pendingStat() { + const gate = Promise.withResolvers() + stat.mockReturnValue(gate.promise) + return gate +} + +async function canceledWait(path = cwd) { + const controller = new AbortController() + const signal = new WeakRef(controller.signal) + const waiting = validate(path, { signal: controller.signal }) + controller.abort() + try { + await waiting + throw new Error('Expected cancellation') + } catch (error) { + if (!(error instanceof WorkingDirectoryValidationAbortedError)) { + throw error + } + expect(getEventListeners(controller.signal, 'abort')).toHaveLength(0) + return { signal, error: new WeakRef(error) } + } +} + +async function collect(): Promise { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + for (let round = 0; round < 6; round += 1) { + await new Promise((resolve) => setImmediate(resolve)) + globalThis.gc() + } +} + +beforeEach(() => { + stat.mockReset() + _resetWorkingDirectoryValidationStateForTest() +}) +afterEach(() => vi.restoreAllMocks()) + +describe('working directory validation waiter lifetime', () => { + it('releases the first caller and subsequent canceled callers while their native stat stays owned', async () => { + const gate = pendingStat() + try { + const first = await canceledWait() + const later: Awaited>[] = [] + for (let index = 0; index < 31; index += 1) { + later.push(await canceledWait()) + } + await collect() + expect(first.signal.deref()).toBeUndefined() + expect(first.error.deref()).toBeUndefined() + expect(later.filter((ref) => ref.signal.deref() || ref.error.deref())).toHaveLength(0) + expect(stat).toHaveBeenCalledOnce() + + const staying = validate(cwd) + expect(stat).toHaveBeenCalledOnce() + gate.resolve(directory) + await staying + } finally { + gate.resolve(directory) + } + }) + + it.each([false, true])( + 'cleans a successful or rejected live waiter: reject=%s', + async (reject) => { + const gate = pendingStat() + const controller = new AbortController() + const raw = validate(cwd) + expect(validate(cwd)).toBe(raw) + const rawResult = raw.catch((error: unknown) => error) + const waiting = validate(cwd, { signal: controller.signal }).catch((error: unknown) => error) + expect(getEventListeners(controller.signal, 'abort')).toHaveLength(1) + if (reject) { + gate.reject(new Error('Native stat failed')) + } else { + gate.resolve(directory) + } + const [rawValue, callerValue] = await Promise.all([rawResult, waiting]) + expect(callerValue).toBe(rawValue) + expect(getEventListeners(controller.signal, 'abort')).toHaveLength(0) + if (reject) { + expect(callerValue).toBeInstanceOf(Error) + } else { + expect(callerValue).toBeUndefined() + } + stat.mockResolvedValue(directory) + await validate(cwd) + expect(stat).toHaveBeenCalledTimes(2) + } + ) + + it('preserves the raw operation when the first caller is already aborted', async () => { + const gate = pendingStat() + try { + const signal = AbortSignal.abort() + await expect(validate(cwd, { signal })).rejects.toBeInstanceOf( + WorkingDirectoryValidationAbortedError + ) + const first = validate(cwd) + expect(validate(cwd)).toBe(first) + expect(stat).toHaveBeenCalledOnce() + expect(getEventListeners(signal, 'abort')).toHaveLength(0) + gate.resolve(directory) + await first + } finally { + gate.resolve(directory) + } + }) + + it('handles native rejection after every caller has already canceled', async () => { + const unhandled: unknown[] = [] + const recordUnhandled = (error: unknown): void => { + unhandled.push(error) + } + process.on('unhandledRejection', recordUnhandled) + const gate = pendingStat() + try { + await canceledWait() + gate.reject(new Error('Late native failure')) + await new Promise((resolve) => setImmediate(resolve)) + expect(unhandled).toEqual([]) + stat.mockResolvedValue(directory) + await validate(cwd) + expect(stat).toHaveBeenCalledTimes(2) + } finally { + gate.resolve(directory) + process.off('unhandledRejection', recordUnhandled) + } + }) + + it('keeps UNC slots occupied after caller cancellation until native settlement', async () => { + const gates = Array.from({ length: 3 }, () => Promise.withResolvers()) + let calls = 0 + stat.mockImplementation(() => { + const gate = gates[calls++] + if (!gate) { + throw new Error('Unexpected native stat') + } + return gate.promise + }) + try { + for (let index = 0; index < 3; index += 1) { + await canceledWait(`\\\\synthetic-host\\path-${index}`) + } + expect(stat).toHaveBeenCalledTimes(2) + gates[0].resolve(directory) + await new Promise((resolve) => setImmediate(resolve)) + expect(stat).toHaveBeenCalledTimes(3) + } finally { + for (const gate of gates) { + gate.resolve(directory) + } + await new Promise((resolve) => setImmediate(resolve)) + } + }) + + it.each( + (['before', 'between', 'after'] as const).flatMap((position) => + [false, true].map((reject) => ({ position, reject })) + ) + )( + 'preserves an external raw observer at $position with reject=$reject', + async ({ position, reject }) => { + const gate = pendingStat() + const raw = validate(cwd) + const controller = new AbortController() + const start = () => + validate(cwd, { signal: controller.signal }).then( + () => 'fulfilled', + (error: unknown) => (error instanceof Error ? error.name : 'unknown') + ) + const waiters: Promise[] = [] + if (position !== 'before') { + waiters.push(start()) + } + const abortObserver = raw.then( + () => controller.abort(), + () => controller.abort() + ) + if (position !== 'after') { + waiters.push(start()) + } + if (reject) { + gate.reject(new Error('Native stat failed')) + } else { + gate.resolve(directory) + } + const rawOutcome = reject ? 'Error' : 'fulfilled' + expect(await Promise.all(waiters)).toEqual( + position === 'before' + ? ['WorkingDirectoryValidationAbortedError'] + : position === 'between' + ? [rawOutcome, 'WorkingDirectoryValidationAbortedError'] + : [rawOutcome] + ) + await abortObserver + } + ) +}) diff --git a/src/main/providers/working-directory-validation.ts b/src/main/providers/working-directory-validation.ts index 688cc852446..cf098cad2c3 100644 --- a/src/main/providers/working-directory-validation.ts +++ b/src/main/providers/working-directory-validation.ts @@ -162,18 +162,7 @@ export function validateWorkingDirectoryAsync( if (!signal) { return validation } - const shared = validation - // The shared probe outlives this caller; keep it from surfacing as unhandled. - void shared.catch(() => {}) - return new Promise((resolve, reject) => { - const onAbort = (): void => reject(new WorkingDirectoryValidationAbortedError(cwd)) - if (signal.aborted) { - onAbort() - return - } - signal.addEventListener('abort', onAbort, { once: true }) - shared.then(resolve, reject).finally(() => signal.removeEventListener('abort', onAbort)) - }) + return waitForWorkingDirectoryValidation(validation, cwd, signal) } function validateWorkingDirectoryUncached(cwd: string): Promise { @@ -202,3 +191,52 @@ async function probeWorkingDirectory(cwd: string): Promise { throw new Error(`Working directory "${cwd}" is not a directory.`) } } + +type WorkingDirectoryWaiterHolder = { + waiter: { + signal: AbortSignal + onAbort: () => void + resolve: () => void + reject: (error: unknown) => void + } | null +} + +function takeWorkingDirectoryWaiter( + holder: WorkingDirectoryWaiterHolder +): WorkingDirectoryWaiterHolder['waiter'] { + const waiter = holder.waiter + holder.waiter = null + waiter?.signal.removeEventListener('abort', waiter.onAbort) + return waiter +} + +// Keep reaction order while an abandoned caller's signal and resolver become collectible. +function observeWorkingDirectoryValidation( + promise: Promise, + holder: WorkingDirectoryWaiterHolder +): void { + void promise.then( + () => takeWorkingDirectoryWaiter(holder)?.resolve(), + (error: unknown) => takeWorkingDirectoryWaiter(holder)?.reject(error) + ) +} + +function waitForWorkingDirectoryValidation( + shared: Promise, + cwd: string, + signal: AbortSignal +): Promise { + return new Promise((resolve, reject) => { + const holder: WorkingDirectoryWaiterHolder = { waiter: null } + const onAbort = (): void => { + takeWorkingDirectoryWaiter(holder)?.reject(new WorkingDirectoryValidationAbortedError(cwd)) + } + holder.waiter = { signal, onAbort, resolve, reject } + if (signal.aborted) { + onAbort() + return + } + signal.addEventListener('abort', onAbort, { once: true }) + observeWorkingDirectoryValidation(shared, holder) + }) +} From 14654d03cb14abbcb6d442b360c403c2cc778bcd Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:12:25 -0700 Subject: [PATCH 058/168] fix: release completed SSH writer queue entries (#21150) Co-authored-by: m4air --- .../ssh-writer-consumed-prefix/README.md | 54 +++ .../before.config.mjs | 24 ++ .../electron-results.json | 382 ++++++++++++++++++ .../ssh-writer-consumed-prefix/fix.patch | 18 + .../node-results.json | 381 +++++++++++++++++ .../ssh-writer-consumed-prefix/reproduce.cjs | 57 +++ .../ssh-writer-consumed-prefix/scenario.cjs | 260 ++++++++++++ .../source-versions.json | 208 ++++++++++ .../ssh-writer-consumed-prefix/sources.cjs | 102 +++++ .../validation.json | 157 +++++++ .../ssh-multiplexer-writer-lane-scheduler.ts | 12 +- .../ssh-multiplexer-writer-retention.test.ts | 159 ++++++++ 12 files changed, 1810 insertions(+), 4 deletions(-) create mode 100644 docs/audits/ssh-writer-consumed-prefix/README.md create mode 100644 docs/audits/ssh-writer-consumed-prefix/before.config.mjs create mode 100644 docs/audits/ssh-writer-consumed-prefix/electron-results.json create mode 100644 docs/audits/ssh-writer-consumed-prefix/fix.patch create mode 100644 docs/audits/ssh-writer-consumed-prefix/node-results.json create mode 100644 docs/audits/ssh-writer-consumed-prefix/reproduce.cjs create mode 100644 docs/audits/ssh-writer-consumed-prefix/scenario.cjs create mode 100644 docs/audits/ssh-writer-consumed-prefix/source-versions.json create mode 100644 docs/audits/ssh-writer-consumed-prefix/sources.cjs create mode 100644 docs/audits/ssh-writer-consumed-prefix/validation.json create mode 100644 src/main/ssh/ssh-multiplexer-writer-retention.test.ts diff --git a/docs/audits/ssh-writer-consumed-prefix/README.md b/docs/audits/ssh-writer-consumed-prefix/README.md new file mode 100644 index 00000000000..cffd00155f9 --- /dev/null +++ b/docs/audits/ssh-writer-consumed-prefix/README.md @@ -0,0 +1,54 @@ +# Completed SSH writes retained behind a rolling backlog + +The SSH multiplexer lane scheduler advanced its read index without clearing consumed entries. A lane that stayed nonempty retained every completed `WriterEntry`, its encoded buffer and its settlement callback. The writer had already released those entries from its byte and frame counters, so admission limits did not bound this consumed prefix. Fully draining the lane or disposing the multiplexer released it. + +The fix clears the selected slot and compacts a consumed prefix after at least 1,024 selections when it occupies at least half the array. This follows the existing `RelayFrameBuffer` pattern. Lane ordering, fairness, admission limits, transport settlement and in-flight ownership are unchanged. `clear()` returns only remaining live entries. + +## Actual caller and ownership + +The ordinary-lane proof executes `writeToSshPtyWithSettlement` → `SshChannelMultiplexer.notifyWithSettlement` → `SshMultiplexerTransportWriter` → `SshMultiplexerWriterLaneScheduler`. `SshPtyProvider` exposes the same helper through its RPC operations. The control-lane proof uses the `git.responseAck` notification shape emitted by `requestGitStreamable`. + +`ssh-relay-deploy-helpers.ts` connects transport writes and settlement callbacks to `channel.stdin.write`, and registers its `drain` event. A producer that keeps at least one queued entry behind repeated backpressure/drain cycles reaches the retained-prefix state. The proof exercises both a controlled callback/drain port and a real Node `Writable` with a deferred write callback and a synthetic 16 KiB high-water mark. No SSH connection, app, window or remote process is launched. + +Selecting an entry transfers scheduler custody to the writer's in-flight set. Clearing its consumed queue slot does not complete the write. A separate control disposes with an in-flight and a queued write: their existing results remain `unverifiable` and `refused`, respectively. The native callback can still retain the in-flight buffer until that callback reference is released. Late and duplicate callbacks do not settle it twice. + +## Results + +Both captured runtimes produce the same counts: Node 26.6 and Electron 43.7 / Node 24.21. Each runs six scenarios before and after the fix. + +| Scenario at the controlled pause | Original | Fixed | +| ----------------------------------------------------------------------- | ---------: | ------: | +| Ordinary lane: completed buffers and settlement callbacks retained | 2,048 each | 0 | +| Control lane: completed buffers and settlement callbacks retained | 2,048 each | 0 | +| Physical queue slots after those selections | 2,050 | 2 | +| Logical queued ordinary frames / bytes | 2 / 706 | 2 / 706 | +| Logical queued control frames / bytes | 2 / 184 | 2 / 184 | +| Real writable: retained written buffers, including one in flight | 128 | 1 | +| In-flight buffer retained after disposal while native callback is owned | 1 | 1 | +| Written buffers retained after complete drain or final callback release | 0 | 0 | + +The real-writable scenario completes 128 writes, starts the 129th and keeps two further writes queued. The first write preceded the rolling backlog and is collectible in both variants; the original therefore retains 127 completed buffers plus the in-flight one. Its logical budget is three frames / 49,440 bytes in both variants. Empty slots below the compaction threshold are expected and do not retain those buffers. + +The scenarios assert FIFO order, isolation of ordinary/control counters, full-drain and disposal cleanup. Five permanent lifetime regressions plus 26 existing tests pass. The original-source overlay fails the four rolling-backlog regressions and passes the other 27 tests. Existing tests cover control priority and starvation prevention, liveness bypass, synchronous drain, callback errors and duplicates, overflow, disposal, timeouts and slow-but-live transport handling. See `validation.json` for commands and full-publication quality checks. + +## Reproduce + +From the repository root after dependency installation: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/ssh-writer-consumed-prefix/reproduce.cjs +``` + +For Electron, run the installed Electron executable with `ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1`, passing the same flags and script path. This is a Node-mode process with no UI. The runner has a 20-second deadline. + +An optional final argument selects the report destination, for example `notes/ssh-writer-consumed-prefix/reviewer-node.json`. Without it, the runner refreshes the corresponding artifact `node-results.json` or `electron-results.json`. + +`sources.cjs` reverses `fix.patch` in memory and verifies both original and fixed SHA-256 hashes. It also verifies all nine bundled source dependencies against the recorded hashes. No source file is rewritten. A synthetic CRLF read of the patch and source must reproduce identical canonical LF sources and hashes. `before.config.mjs` uses the same source loader for the original-source test overlay. + +## Source compatibility and scope + +`source-versions.json` records the exact scheduler baseline at the pre-fix audit commit, independent main `291b4ddd6f1c1af480169885e0fda7f9c78ff053`, and v1.4.198 `e0826956fcfc532f5a1e55b5e081f2e57e553c43`. These scheduler sources are byte-identical after LF normalization, so the same patch yields the same fixed hash. All nine bundled sources and three additional caller sources match independent main. The projected main change has no dependency on the other memory-audit fixes. + +The v1.4.198 scheduler is identical, and its writer contains the same enqueue/select/release path. Seven of the twelve dependency/caller files differ from current source; this artifact does not claim to execute the packaged historical release. + +Inputs and timing are controlled fixtures. Reachability counts establish the code-level retention mechanism; they do not measure affected-host RSS, model a reported growth rate, or establish that an incident had a continuously nonempty SSH write lane. Active pending writes, transport-owned callbacks and retained primitive sequence timestamps remain governed by their existing limits and lifecycles. diff --git a/docs/audits/ssh-writer-consumed-prefix/before.config.mjs b/docs/audits/ssh-writer-consumed-prefix/before.config.mjs new file mode 100644 index 00000000000..7fc8034a383 --- /dev/null +++ b/docs/audits/ssh-writer-consumed-prefix/before.config.mjs @@ -0,0 +1,24 @@ +import { resolve } from 'node:path' +import { createRequire } from 'node:module' +import { defineConfig, mergeConfig } from 'vitest/config' +import baseConfig from '../../../config/vitest.config.ts' + +const { loadSources } = createRequire(import.meta.url)( + resolve('docs/audits/ssh-writer-consumed-prefix/sources.cjs') +) +const { before } = loadSources() +export default mergeConfig( + baseConfig, + defineConfig({ + plugins: [ + { + name: 'ssh-scheduler-before-fix', + enforce: 'pre', + transform(_code, id) { + const source = before.get(resolve(id.split('?')[0])) + return source === undefined ? undefined : { code: source, map: null } + } + } + ] + }) +) diff --git a/docs/audits/ssh-writer-consumed-prefix/electron-results.json b/docs/audits/ssh-writer-consumed-prefix/electron-results.json new file mode 100644 index 00000000000..c067efd1211 --- /dev/null +++ b/docs/audits/ssh-writer-consumed-prefix/electron-results.json @@ -0,0 +1,382 @@ +{ + "runtime": { + "node": "24.21.0", + "acorn": "8.18.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.2", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "148", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "0.0.0", + "simdjson": "4.6.7", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2025c", + "undici": "7.29.1", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "15.0.245.31-electron.0", + "zlib": "1.3.2.1-motley", + "zstd": "1.6.0", + "electron": "43.7.0", + "chrome": "150.0.7871.250" + }, + "sourceHashLineEndings": "canonical LF", + "crlfLoaderControl": { + "syntheticCrlfReads": 2, + "identicalSourcesAndHashes": true + }, + "cases": [ + { + "fixed": false, + "release": "drain", + "laneName": "ordinary", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 2048, + "completedCallbacksAlive": 2048, + "logicalFrames": 2, + "logicalBytes": 706, + "physicalSlots": 2050, + "head": 2048, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": false + } + }, + { + "fixed": false, + "release": "dispose", + "laneName": "ordinary", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 2048, + "completedCallbacksAlive": 2048, + "logicalFrames": 2, + "logicalBytes": 706, + "physicalSlots": 2050, + "head": 2048, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": true + } + }, + { + "fixed": false, + "release": "drain", + "laneName": "control", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 2048, + "completedCallbacksAlive": 2048, + "logicalFrames": 2, + "logicalBytes": 184, + "physicalSlots": 2050, + "head": 2048, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": false + } + }, + { + "fixed": false, + "release": "dispose", + "laneName": "control", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 2048, + "completedCallbacksAlive": 2048, + "logicalFrames": 2, + "logicalBytes": 184, + "physicalSlots": 2050, + "head": 2048, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": true + } + }, + { + "kind": "real-node-writable", + "fixed": false, + "writes": 129, + "accepted": 128, + "logicalFrames": 3, + "logicalBytes": 49440, + "physicalSlots": 130, + "retainedBuffers": 128 + }, + { + "kind": "in-flight-callback-ownership", + "fixed": false, + "retainedByNativeCallback": 1, + "afterNativeCallbackRelease": 0, + "pendingResult": { + "outcome": "unverifiable", + "reason": "transport_settlement_lost", + "bytesHandedToTransport": true + }, + "queuedResult": { + "outcome": "refused", + "reason": "transport_rejected_before_handoff" + } + }, + { + "fixed": true, + "release": "drain", + "laneName": "ordinary", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 0, + "completedCallbacksAlive": 0, + "logicalFrames": 2, + "logicalBytes": 706, + "physicalSlots": 2, + "head": 0, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": false + } + }, + { + "fixed": true, + "release": "dispose", + "laneName": "ordinary", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 0, + "completedCallbacksAlive": 0, + "logicalFrames": 2, + "logicalBytes": 706, + "physicalSlots": 2, + "head": 0, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": true + } + }, + { + "fixed": true, + "release": "drain", + "laneName": "control", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 0, + "completedCallbacksAlive": 0, + "logicalFrames": 2, + "logicalBytes": 184, + "physicalSlots": 2, + "head": 0, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": false + } + }, + { + "fixed": true, + "release": "dispose", + "laneName": "control", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 0, + "completedCallbacksAlive": 0, + "logicalFrames": 2, + "logicalBytes": 184, + "physicalSlots": 2, + "head": 0, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": true + } + }, + { + "kind": "real-node-writable", + "fixed": true, + "writes": 129, + "accepted": 128, + "logicalFrames": 3, + "logicalBytes": 49440, + "physicalSlots": 130, + "retainedBuffers": 1 + }, + { + "kind": "in-flight-callback-ownership", + "fixed": true, + "retainedByNativeCallback": 1, + "afterNativeCallbackRelease": 0, + "pendingResult": { + "outcome": "unverifiable", + "reason": "transport_settlement_lost", + "bytesHandedToTransport": true + }, + "queuedResult": { + "outcome": "refused", + "reason": "transport_rejected_before_handoff" + } + } + ], + "versions": [ + { + "fixed": false, + "sourceHashes": { + "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts": { + "before": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50", + "after": "6173ddd6640a930d0fa586fed91c1adc0d865a33f81af9a1ed5a0817c08e90cc" + } + }, + "bundleSha256": "aafe581371b67c00d50d96a0857257bee20fa317c2d8ce4ea1800e558c7a1b91", + "dependencies": [ + { + "path": "src/shared/relay-frame-decoder-contract.ts", + "sha256": "f58279099fe4dbbdfde9e3e038a916ae077e71d8ac6aa871b59a64c6fbaeaad8" + }, + { + "path": "src/shared/relay-frame-buffer.ts", + "sha256": "f89fcd33489894c34f79ba8fd5e5634487555a7c34fc8e7c054229ae1c20e0be" + }, + { + "path": "src/shared/relay-frame-decoder.ts", + "sha256": "88c0ecbc06b93efe6733094b8c9e4509ab1cbd139f8cb3257e9fb74a0ed39194" + }, + { + "path": "src/main/ssh/relay-protocol.ts", + "sha256": "644aa6f2087b5867d41006bfdcec78ffba693157a2912feb8f82b120f5647b34" + }, + { + "path": "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "sha256": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50" + }, + { + "path": "src/shared/pty-write-settlement.ts", + "sha256": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6" + }, + { + "path": "src/main/ssh/ssh-multiplexer-transport-writer.ts", + "sha256": "433a2cc400b5ed9743871ca03aac06d31a079ef23c42c2a2759f78967312d071" + }, + { + "path": "src/main/ssh/ssh-channel-multiplexer.ts", + "sha256": "9ecd88963fec72901596bd25d2cb4c8666ef41d3c8a968242c6eef52095ee108" + }, + { + "path": "src/main/providers/ssh-pty-write.ts", + "sha256": "357024da7dc5bf2df1dabd43f6f90146afa239d414e29f04b17f86a6351520ae" + } + ] + }, + { + "fixed": true, + "sourceHashes": { + "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts": { + "before": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50", + "after": "6173ddd6640a930d0fa586fed91c1adc0d865a33f81af9a1ed5a0817c08e90cc" + } + }, + "bundleSha256": "8dad9c7df8842e01466d24e238ed08b8cfc642fbc3f0a6867ea2ec49058d2aa8", + "dependencies": [ + { + "path": "src/shared/relay-frame-decoder-contract.ts", + "sha256": "f58279099fe4dbbdfde9e3e038a916ae077e71d8ac6aa871b59a64c6fbaeaad8" + }, + { + "path": "src/shared/relay-frame-buffer.ts", + "sha256": "f89fcd33489894c34f79ba8fd5e5634487555a7c34fc8e7c054229ae1c20e0be" + }, + { + "path": "src/shared/relay-frame-decoder.ts", + "sha256": "88c0ecbc06b93efe6733094b8c9e4509ab1cbd139f8cb3257e9fb74a0ed39194" + }, + { + "path": "src/main/ssh/relay-protocol.ts", + "sha256": "644aa6f2087b5867d41006bfdcec78ffba693157a2912feb8f82b120f5647b34" + }, + { + "path": "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "sha256": "6173ddd6640a930d0fa586fed91c1adc0d865a33f81af9a1ed5a0817c08e90cc" + }, + { + "path": "src/shared/pty-write-settlement.ts", + "sha256": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6" + }, + { + "path": "src/main/ssh/ssh-multiplexer-transport-writer.ts", + "sha256": "433a2cc400b5ed9743871ca03aac06d31a079ef23c42c2a2759f78967312d071" + }, + { + "path": "src/main/ssh/ssh-channel-multiplexer.ts", + "sha256": "9ecd88963fec72901596bd25d2cb4c8666ef41d3c8a968242c6eef52095ee108" + }, + { + "path": "src/main/providers/ssh-pty-write.ts", + "sha256": "357024da7dc5bf2df1dabd43f6f90146afa239d414e29f04b17f86a6351520ae" + } + ] + } + ] +} diff --git a/docs/audits/ssh-writer-consumed-prefix/fix.patch b/docs/audits/ssh-writer-consumed-prefix/fix.patch new file mode 100644 index 00000000000..90244923c94 --- /dev/null +++ b/docs/audits/ssh-writer-consumed-prefix/fix.patch @@ -0,0 +1,18 @@ +--- a/src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts ++++ b/src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts +@@ -6 +6 @@ +- entries: T[] ++ entries: (T | undefined)[] +@@ -22,0 +23 @@ ++ queue.entries[queue.head] = undefined +@@ -24,2 +25,5 @@ +- if (queue.head === queue.entries.length) { +- queue.entries.length = 0 ++ if ( ++ queue.head === queue.entries.length || ++ (queue.head >= 1024 && queue.head * 2 >= queue.entries.length) ++ ) { ++ queue.entries = queue.entries.slice(queue.head) +@@ -32 +36 @@ +- const entries = queue.entries.slice(queue.head) ++ const entries = queue.entries.slice(queue.head).filter((entry): entry is T => entry !== undefined) diff --git a/docs/audits/ssh-writer-consumed-prefix/node-results.json b/docs/audits/ssh-writer-consumed-prefix/node-results.json new file mode 100644 index 00000000000..ef533d6c384 --- /dev/null +++ b/docs/audits/ssh-writer-consumed-prefix/node-results.json @@ -0,0 +1,381 @@ +{ + "runtime": { + "node": "26.6.0", + "acorn": "8.17.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.3", + "libffi": "3.7.1", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "147", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "3.6.3", + "simdjson": "4.6.6", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2026a", + "undici": "8.9.0", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "14.6.202.34-node.26", + "zlib": "1.2.12", + "zstd": "1.5.7" + }, + "sourceHashLineEndings": "canonical LF", + "crlfLoaderControl": { + "syntheticCrlfReads": 2, + "identicalSourcesAndHashes": true + }, + "cases": [ + { + "fixed": false, + "release": "drain", + "laneName": "ordinary", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 2048, + "completedCallbacksAlive": 2048, + "logicalFrames": 2, + "logicalBytes": 706, + "physicalSlots": 2050, + "head": 2048, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": false + } + }, + { + "fixed": false, + "release": "dispose", + "laneName": "ordinary", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 2048, + "completedCallbacksAlive": 2048, + "logicalFrames": 2, + "logicalBytes": 706, + "physicalSlots": 2050, + "head": 2048, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": true + } + }, + { + "fixed": false, + "release": "drain", + "laneName": "control", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 2048, + "completedCallbacksAlive": 2048, + "logicalFrames": 2, + "logicalBytes": 184, + "physicalSlots": 2050, + "head": 2048, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": false + } + }, + { + "fixed": false, + "release": "dispose", + "laneName": "control", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 2048, + "completedCallbacksAlive": 2048, + "logicalFrames": 2, + "logicalBytes": 184, + "physicalSlots": 2050, + "head": 2048, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": true + } + }, + { + "kind": "real-node-writable", + "fixed": false, + "writes": 129, + "accepted": 128, + "logicalFrames": 3, + "logicalBytes": 49440, + "physicalSlots": 130, + "retainedBuffers": 128 + }, + { + "kind": "in-flight-callback-ownership", + "fixed": false, + "retainedByNativeCallback": 1, + "afterNativeCallbackRelease": 0, + "pendingResult": { + "outcome": "unverifiable", + "reason": "transport_settlement_lost", + "bytesHandedToTransport": true + }, + "queuedResult": { + "outcome": "refused", + "reason": "transport_rejected_before_handoff" + } + }, + { + "fixed": true, + "release": "drain", + "laneName": "ordinary", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 0, + "completedCallbacksAlive": 0, + "logicalFrames": 2, + "logicalBytes": 706, + "physicalSlots": 2, + "head": 0, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": false + } + }, + { + "fixed": true, + "release": "dispose", + "laneName": "ordinary", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 0, + "completedCallbacksAlive": 0, + "logicalFrames": 2, + "logicalBytes": 706, + "physicalSlots": 2, + "head": 0, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": true + } + }, + { + "fixed": true, + "release": "drain", + "laneName": "control", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 0, + "completedCallbacksAlive": 0, + "logicalFrames": 2, + "logicalBytes": 184, + "physicalSlots": 2, + "head": 0, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": false + } + }, + { + "fixed": true, + "release": "dispose", + "laneName": "control", + "during": { + "completedWrites": 2049, + "accepted": 2049, + "completedBuffersAlive": 0, + "completedCallbacksAlive": 0, + "logicalFrames": 2, + "logicalBytes": 184, + "physicalSlots": 2, + "head": 0, + "liveQueued": 2, + "disposed": false + }, + "after": { + "completedBuffersAlive": 0, + "physicalSlots": 0, + "logicalFrames": 0, + "logicalBytes": 0, + "disposed": true + } + }, + { + "kind": "real-node-writable", + "fixed": true, + "writes": 129, + "accepted": 128, + "logicalFrames": 3, + "logicalBytes": 49440, + "physicalSlots": 130, + "retainedBuffers": 1 + }, + { + "kind": "in-flight-callback-ownership", + "fixed": true, + "retainedByNativeCallback": 1, + "afterNativeCallbackRelease": 0, + "pendingResult": { + "outcome": "unverifiable", + "reason": "transport_settlement_lost", + "bytesHandedToTransport": true + }, + "queuedResult": { + "outcome": "refused", + "reason": "transport_rejected_before_handoff" + } + } + ], + "versions": [ + { + "fixed": false, + "sourceHashes": { + "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts": { + "before": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50", + "after": "6173ddd6640a930d0fa586fed91c1adc0d865a33f81af9a1ed5a0817c08e90cc" + } + }, + "bundleSha256": "aafe581371b67c00d50d96a0857257bee20fa317c2d8ce4ea1800e558c7a1b91", + "dependencies": [ + { + "path": "src/shared/relay-frame-decoder-contract.ts", + "sha256": "f58279099fe4dbbdfde9e3e038a916ae077e71d8ac6aa871b59a64c6fbaeaad8" + }, + { + "path": "src/shared/relay-frame-buffer.ts", + "sha256": "f89fcd33489894c34f79ba8fd5e5634487555a7c34fc8e7c054229ae1c20e0be" + }, + { + "path": "src/shared/relay-frame-decoder.ts", + "sha256": "88c0ecbc06b93efe6733094b8c9e4509ab1cbd139f8cb3257e9fb74a0ed39194" + }, + { + "path": "src/main/ssh/relay-protocol.ts", + "sha256": "644aa6f2087b5867d41006bfdcec78ffba693157a2912feb8f82b120f5647b34" + }, + { + "path": "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "sha256": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50" + }, + { + "path": "src/shared/pty-write-settlement.ts", + "sha256": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6" + }, + { + "path": "src/main/ssh/ssh-multiplexer-transport-writer.ts", + "sha256": "433a2cc400b5ed9743871ca03aac06d31a079ef23c42c2a2759f78967312d071" + }, + { + "path": "src/main/ssh/ssh-channel-multiplexer.ts", + "sha256": "9ecd88963fec72901596bd25d2cb4c8666ef41d3c8a968242c6eef52095ee108" + }, + { + "path": "src/main/providers/ssh-pty-write.ts", + "sha256": "357024da7dc5bf2df1dabd43f6f90146afa239d414e29f04b17f86a6351520ae" + } + ] + }, + { + "fixed": true, + "sourceHashes": { + "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts": { + "before": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50", + "after": "6173ddd6640a930d0fa586fed91c1adc0d865a33f81af9a1ed5a0817c08e90cc" + } + }, + "bundleSha256": "8dad9c7df8842e01466d24e238ed08b8cfc642fbc3f0a6867ea2ec49058d2aa8", + "dependencies": [ + { + "path": "src/shared/relay-frame-decoder-contract.ts", + "sha256": "f58279099fe4dbbdfde9e3e038a916ae077e71d8ac6aa871b59a64c6fbaeaad8" + }, + { + "path": "src/shared/relay-frame-buffer.ts", + "sha256": "f89fcd33489894c34f79ba8fd5e5634487555a7c34fc8e7c054229ae1c20e0be" + }, + { + "path": "src/shared/relay-frame-decoder.ts", + "sha256": "88c0ecbc06b93efe6733094b8c9e4509ab1cbd139f8cb3257e9fb74a0ed39194" + }, + { + "path": "src/main/ssh/relay-protocol.ts", + "sha256": "644aa6f2087b5867d41006bfdcec78ffba693157a2912feb8f82b120f5647b34" + }, + { + "path": "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "sha256": "6173ddd6640a930d0fa586fed91c1adc0d865a33f81af9a1ed5a0817c08e90cc" + }, + { + "path": "src/shared/pty-write-settlement.ts", + "sha256": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6" + }, + { + "path": "src/main/ssh/ssh-multiplexer-transport-writer.ts", + "sha256": "433a2cc400b5ed9743871ca03aac06d31a079ef23c42c2a2759f78967312d071" + }, + { + "path": "src/main/ssh/ssh-channel-multiplexer.ts", + "sha256": "9ecd88963fec72901596bd25d2cb4c8666ef41d3c8a968242c6eef52095ee108" + }, + { + "path": "src/main/providers/ssh-pty-write.ts", + "sha256": "357024da7dc5bf2df1dabd43f6f90146afa239d414e29f04b17f86a6351520ae" + } + ] + } + ] +} diff --git a/docs/audits/ssh-writer-consumed-prefix/reproduce.cjs b/docs/audits/ssh-writer-consumed-prefix/reproduce.cjs new file mode 100644 index 00000000000..5cb0af998da --- /dev/null +++ b/docs/audits/ssh-writer-consumed-prefix/reproduce.cjs @@ -0,0 +1,57 @@ +const assert = require('node:assert/strict') +const { readFileSync, writeFileSync } = require('node:fs') +const path = require('node:path') +const { load, loadSources, canonicalLf } = require('./sources.cjs') +const { scenario, realWritableScenario, inFlightOwnership } = require('./scenario.cjs') +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function') +function checkCrlfLoader() { + const normal = loadSources() + let syntheticCrlfReads = 0 + const crlf = loadSources({ + readText(file) { + syntheticCrlfReads++ + return canonicalLf(readFileSync(file, 'utf8')).replace(/\n/g, '\r\n') + } + }) + assert.equal(syntheticCrlfReads, 2) + assert.deepEqual(crlf.before, normal.before) + assert.deepEqual(crlf.after, normal.after) + assert.deepEqual(crlf.hashes, normal.hashes) + return { syntheticCrlfReads, identicalSourcesAndHashes: true } +} +async function main() { + const timer = setTimeout(() => { + process.stderr.write('deadline\n') + process.exit(2) + }, 20_000) + const output = { + runtime: process.versions, + sourceHashLineEndings: 'canonical LF', + crlfLoaderControl: checkCrlfLoader(), + cases: [], + versions: [] + } + for (const fixed of [false, true]) { + const api = await load(fixed) + output.versions.push({ fixed, ...api.versions }) + for (const lane of ['ordinary', 'control']) { + for (const release of ['drain', 'dispose']) { + output.cases.push(await scenario(api, fixed, release, lane)) + } + } + output.cases.push(await realWritableScenario(api, fixed)) + output.cases.push(await inFlightOwnership(api, fixed)) + } + clearTimeout(timer) + const defaultName = process.versions.electron ? 'electron-results.json' : 'node-results.json' + const destination = process.argv[2] + ? path.resolve(process.argv[2]) + : path.join(__dirname, defaultName) + writeFileSync(destination, `${JSON.stringify(output, null, 2)}\n`) + process.stdout.write(`${JSON.stringify(output.cases, null, 2)}\n`) +} +main().catch((error) => { + process.stderr.write(`${error.stack}\n`) + process.exit(1) +}) diff --git a/docs/audits/ssh-writer-consumed-prefix/scenario.cjs b/docs/audits/ssh-writer-consumed-prefix/scenario.cjs new file mode 100644 index 00000000000..c43d2c4087c --- /dev/null +++ b/docs/audits/ssh-writer-consumed-prefix/scenario.cjs @@ -0,0 +1,260 @@ +const assert = require('node:assert/strict') +const { Writable } = require('node:stream') +const pause = () => new Promise((resolve) => setImmediate(resolve)) +async function collect() { + for (let i = 0; i < 8; i++) { + await pause() + global.gc() + } + await pause() +} +async function scenario(api, fixed, release, laneName) { + let drain, + current, + writes = 0, + next = 0, + accepted = 0 + const weak = [], + callbacks = [] + const mux = new api.SshChannelMultiplexer({ + supportsWriteSettlement: true, + write(bytes, settle) { + assert.equal(current, undefined) + const msg = JSON.parse(bytes.subarray(13).toString()) + assert.equal( + laneName === 'ordinary' ? Number.parseInt(msg.params.data, 10) : msg.params.seq, + writes + ) + weak.push(new WeakRef(bytes)) + callbacks.push(new WeakRef([...mux.writer.inFlight][0].onSettled)) + current = settle + writes++ + return false + }, + onDrain(fn) { + drain = fn + return () => { + drain = undefined + } + }, + onData() {}, + onClose() {}, + pauseReads() {}, + resumeReads() {}, + close() {} + }) + const enqueue = () => { + const id = next++ + if (laneName === 'ordinary') { + const promise = api.writeToSshPtyWithSettlement( + mux, + 'synthetic-pty', + `${id}:${'x'.repeat(256)}` + ) + void promise.then((result) => { + if (result.outcome === 'accepted') { + accepted++ + } + }) + } else { + mux.notify('git.responseAck', { streamId: 1, seq: id }) + } + } + const settle = () => { + assert.ok(current) + const cb = current + current = undefined + cb({ ok: true }) + if (laneName === 'control') { + accepted++ + } + } + enqueue() + enqueue() + enqueue() + settle() + for (let i = 0; i < 2048; i++) { + drain() + settle() + enqueue() + } + await collect() + const lane = mux.writer.scheduler[laneName] + const completedAlive = weak.reduce((n, w) => n + (w.deref() !== undefined), 0) + const during = { + completedWrites: writes, + accepted, + completedBuffersAlive: completedAlive, + completedCallbacksAlive: callbacks.reduce((n, w) => n + (w.deref() !== undefined), 0), + logicalFrames: mux.writer[`${laneName}Frames`], + logicalBytes: mux.writer[`${laneName}Bytes`], + physicalSlots: lane.entries.length, + head: lane.head, + liveQueued: lane.entries.length - lane.head, + disposed: mux.isDisposed() + } + assert.equal(during.liveQueued, 2) + assert.equal(during.logicalFrames, 2) + assert.equal(during.disposed, false) + assert.equal(accepted, writes) + assert.equal(completedAlive, fixed ? 0 : 2048) + assert.equal(during.completedCallbacksAlive, fixed ? 0 : 2048) + const sibling = laneName === 'ordinary' ? 'control' : 'ordinary' + assert.equal(mux.writer.scheduler[sibling].entries.length, 0) + assert.equal(mux.writer[`${sibling}Frames`], 0) + if (release === 'drain') { + drain() + settle() + drain() + settle() + } else { + mux.dispose() + } + await collect() + const after = { + completedBuffersAlive: weak.reduce((n, w) => n + (w.deref() !== undefined), 0), + physicalSlots: lane.entries.length, + logicalFrames: mux.writer[`${laneName}Frames`], + logicalBytes: mux.writer[`${laneName}Bytes`], + disposed: mux.isDisposed() + } + assert.equal(after.completedBuffersAlive, 0) + assert.equal(after.physicalSlots, 0) + assert.equal(after.logicalFrames, 0) + mux.dispose() + return { fixed, release, laneName, during, after } +} + +async function realWritableScenario(api, fixed) { + let complete, + writes = 0, + accepted = 0 + const weak = [] + const sink = new Writable({ + highWaterMark: 16 * 1024, + write(bytes, encoding, callback) { + assert.equal(complete, undefined) + assert.equal( + Number.parseInt(JSON.parse(bytes.subarray(13).toString()).params.data, 10), + writes + ) + weak.push(new WeakRef(bytes)) + writes++ + complete = callback + } + }) + const mux = new api.SshChannelMultiplexer({ + supportsWriteSettlement: true, + write: (bytes, onSettled) => + sink.write(bytes, (error) => onSettled(error ? { ok: false, error } : { ok: true })), + onDrain: (fn) => { + sink.on('drain', fn) + return () => sink.off('drain', fn) + }, + onData() {}, + onClose() {}, + close() {} + }) + let next = 0 + const enqueue = () => + void api + .writeToSshPtyWithSettlement(mux, 'synthetic-pty', `${next++}:${'x'.repeat(16 * 1024)}`) + .then((result) => { + if (result.outcome === 'accepted') { + accepted++ + } + }) + const completeWrite = async () => { + assert.ok(complete) + const cb = complete + complete = undefined + cb() + await pause() + } + enqueue() + enqueue() + enqueue() + for (let i = 0; i < 128; i++) { + await completeWrite() + enqueue() + } + await collect() + const retained = weak.reduce((n, w) => n + (w.deref() !== undefined), 0) + const result = { + kind: 'real-node-writable', + fixed, + writes, + accepted, + logicalFrames: mux.writer.ordinaryFrames, + logicalBytes: mux.writer.ordinaryBytes, + physicalSlots: mux.writer.scheduler.ordinary.entries.length, + retainedBuffers: retained + } + assert.equal(accepted, 128) + assert.equal(writes, 129) + assert.equal(result.logicalFrames, 3) + assert.equal(retained, fixed ? 1 : 128) + while (complete) { + await completeWrite() + } + await collect() + assert.equal( + weak.reduce((n, w) => n + (w.deref() !== undefined), 0), + 0 + ) + assert.equal(mux.writer.scheduler.ordinary.entries.length, 0) + assert.equal(mux.writer.ordinaryFrames, 0) + mux.dispose() + sink.destroy() + return result +} +async function inFlightOwnership(api, fixed) { + let current, drain + const weak = [] + const mux = new api.SshChannelMultiplexer({ + supportsWriteSettlement: true, + write(bytes, fn) { + weak.push(new WeakRef(bytes)) + current = fn + return false + }, + onDrain(fn) { + drain = fn + return () => { + drain = undefined + } + }, + onData() {}, + onClose() {}, + close() {} + }) + const pending = api.writeToSshPtyWithSettlement(mux, 'synthetic-pty', 'in-flight') + const queued = api.writeToSshPtyWithSettlement(mux, 'synthetic-pty', 'queued') + mux.dispose() + const pendingResult = await pending, + queuedResult = await queued + assert.equal(pendingResult.outcome, 'unverifiable') + assert.equal(queuedResult.outcome, 'refused') + await collect() + const retainedByNativeCallback = weak.reduce((n, w) => n + (w.deref() !== undefined), 0) + assert.equal(retainedByNativeCallback, 1) + assert.equal(drain, undefined) + current({ ok: true }) + current({ ok: false, error: new Error('synthetic late duplicate') }) + current = undefined + await collect() + assert.equal( + weak.reduce((n, w) => n + (w.deref() !== undefined), 0), + 0 + ) + return { + kind: 'in-flight-callback-ownership', + fixed, + retainedByNativeCallback, + afterNativeCallbackRelease: 0, + pendingResult, + queuedResult + } +} + +module.exports = { scenario, realWritableScenario, inFlightOwnership } diff --git a/docs/audits/ssh-writer-consumed-prefix/source-versions.json b/docs/audits/ssh-writer-consumed-prefix/source-versions.json new file mode 100644 index 00000000000..7dc96bfd77d --- /dev/null +++ b/docs/audits/ssh-writer-consumed-prefix/source-versions.json @@ -0,0 +1,208 @@ +{ + "baselineHashes": { + "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50" + }, + "fixedHashes": { + "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts": "6173ddd6640a930d0fa586fed91c1adc0d865a33f81af9a1ed5a0817c08e90cc" + }, + "sourceHashLineEndings": "canonical LF", + "namedRefs": { + "2e83de3154c4ee1bbeea816734b892c34500a5cc": { + "path": "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "sha256": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50", + "equalsBaseline": true + }, + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "path": "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "sha256": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50", + "equalsBaseline": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "path": "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "sha256": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50", + "equalsBaseline": true + } + }, + "provenance": [ + { + "path": "src/shared/relay-frame-decoder-contract.ts", + "currentBaselineSha256": "f58279099fe4dbbdfde9e3e038a916ae077e71d8ac6aa871b59a64c6fbaeaad8", + "namedRefs": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "sha256": "f58279099fe4dbbdfde9e3e038a916ae077e71d8ac6aa871b59a64c6fbaeaad8", + "identical": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "sha256": "f58279099fe4dbbdfde9e3e038a916ae077e71d8ac6aa871b59a64c6fbaeaad8", + "identical": true + } + }, + "bundled": true + }, + { + "path": "src/shared/relay-frame-buffer.ts", + "currentBaselineSha256": "f89fcd33489894c34f79ba8fd5e5634487555a7c34fc8e7c054229ae1c20e0be", + "namedRefs": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "sha256": "f89fcd33489894c34f79ba8fd5e5634487555a7c34fc8e7c054229ae1c20e0be", + "identical": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "sha256": "4d40ca7cb812e0af6edf78340b017a150ce9529b71396e4c946358f4ed698f81", + "identical": false + } + }, + "bundled": true + }, + { + "path": "src/shared/relay-frame-decoder.ts", + "currentBaselineSha256": "88c0ecbc06b93efe6733094b8c9e4509ab1cbd139f8cb3257e9fb74a0ed39194", + "namedRefs": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "sha256": "88c0ecbc06b93efe6733094b8c9e4509ab1cbd139f8cb3257e9fb74a0ed39194", + "identical": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "sha256": "88c0ecbc06b93efe6733094b8c9e4509ab1cbd139f8cb3257e9fb74a0ed39194", + "identical": true + } + }, + "bundled": true + }, + { + "path": "src/main/ssh/relay-protocol.ts", + "currentBaselineSha256": "644aa6f2087b5867d41006bfdcec78ffba693157a2912feb8f82b120f5647b34", + "namedRefs": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "sha256": "644aa6f2087b5867d41006bfdcec78ffba693157a2912feb8f82b120f5647b34", + "identical": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "sha256": "644aa6f2087b5867d41006bfdcec78ffba693157a2912feb8f82b120f5647b34", + "identical": true + } + }, + "bundled": true + }, + { + "path": "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "currentBaselineSha256": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50", + "namedRefs": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "sha256": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50", + "identical": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "sha256": "3e5fe7a1e3537505baf42869449193fc0448339d851254a49527ec2a1c7cbb50", + "identical": true + } + }, + "bundled": true + }, + { + "path": "src/shared/pty-write-settlement.ts", + "currentBaselineSha256": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "namedRefs": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "sha256": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "identical": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "sha256": null, + "identical": false + } + }, + "bundled": true + }, + { + "path": "src/main/ssh/ssh-multiplexer-transport-writer.ts", + "currentBaselineSha256": "433a2cc400b5ed9743871ca03aac06d31a079ef23c42c2a2759f78967312d071", + "namedRefs": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "sha256": "433a2cc400b5ed9743871ca03aac06d31a079ef23c42c2a2759f78967312d071", + "identical": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "sha256": "4a73e2194f15ec0604802fe6810742f34930fc55e542458b6d8ab7344ee841a2", + "identical": false + } + }, + "bundled": true + }, + { + "path": "src/main/ssh/ssh-channel-multiplexer.ts", + "currentBaselineSha256": "9ecd88963fec72901596bd25d2cb4c8666ef41d3c8a968242c6eef52095ee108", + "namedRefs": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "sha256": "9ecd88963fec72901596bd25d2cb4c8666ef41d3c8a968242c6eef52095ee108", + "identical": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "sha256": "480c722b27dd1ffb8c70bfca8fb3568294ff2777b7b02607548df93bb280f6ae", + "identical": false + } + }, + "bundled": true + }, + { + "path": "src/main/providers/ssh-pty-write.ts", + "currentBaselineSha256": "357024da7dc5bf2df1dabd43f6f90146afa239d414e29f04b17f86a6351520ae", + "namedRefs": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "sha256": "357024da7dc5bf2df1dabd43f6f90146afa239d414e29f04b17f86a6351520ae", + "identical": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "sha256": "76c4994509a0de61052ceb984a8a9fa0f958bd6a87239ea8e95ff7702f242c67", + "identical": false + } + }, + "bundled": true + }, + { + "path": "src/main/ssh/ssh-relay-deploy-helpers.ts", + "currentBaselineSha256": "5452b8a441268a42abe09ae71ae64c5684e070a7469eed0b421ab4cd8e41abae", + "namedRefs": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "sha256": "5452b8a441268a42abe09ae71ae64c5684e070a7469eed0b421ab4cd8e41abae", + "identical": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "sha256": "d9f42be19fb6c1a7921d2f0258f4b4dea814e0dd19f228616d1a2c4158a7c41e", + "identical": false + } + }, + "bundled": false + }, + { + "path": "src/main/ssh/ssh-git-response-stream-reader.ts", + "currentBaselineSha256": "8e3a2a5606f95ce1c70c1397ca5a807d90989074ea2be41497785c269381af0e", + "namedRefs": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "sha256": "8e3a2a5606f95ce1c70c1397ca5a807d90989074ea2be41497785c269381af0e", + "identical": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "sha256": "8e3a2a5606f95ce1c70c1397ca5a807d90989074ea2be41497785c269381af0e", + "identical": true + } + }, + "bundled": false + }, + { + "path": "src/main/providers/ssh-pty-provider.ts", + "currentBaselineSha256": "7bf1a9e41b606dcfd73bd2a4aa9d9aa185b12f18f9beb027b640fe10cf360123", + "namedRefs": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "sha256": "7bf1a9e41b606dcfd73bd2a4aa9d9aa185b12f18f9beb027b640fe10cf360123", + "identical": true + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "sha256": "f37a0e8688544a79b35e2d2342282f215cd80928adb9da4824d185598fb8a6b6", + "identical": false + } + }, + "bundled": false + } + ] +} diff --git a/docs/audits/ssh-writer-consumed-prefix/sources.cjs b/docs/audits/ssh-writer-consumed-prefix/sources.cjs new file mode 100644 index 00000000000..8518863c55c --- /dev/null +++ b/docs/audits/ssh-writer-consumed-prefix/sources.cjs @@ -0,0 +1,102 @@ +const assert = require('node:assert/strict') +const { readFileSync } = require('node:fs') +const { createHash } = require('node:crypto') +const path = require('node:path') +const Module = require('node:module') +const esbuild = require('esbuild') +const { applyPatch, parsePatch, reversePatch } = require('diff') +const { resolve } = path +const root = resolve(__dirname, '../../..') +const canonicalLf = (text) => text.replace(/\r\n/g, '\n') +const read = (file) => canonicalLf(readFileSync(file, 'utf8')) +const sha = (value) => createHash('sha256').update(value).digest('hex') +const sourcePath = 'src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts' + +function loadSources({ readText = (file) => readFileSync(file, 'utf8') } = {}) { + const root = resolve(__dirname, '../../..') + const expected = JSON.parse(readFileSync(resolve(__dirname, 'source-versions.json'), 'utf8')) + const parsed = parsePatch(canonicalLf(readText(resolve(__dirname, 'fix.patch')))) + const before = new Map() + const after = new Map() + const hashes = {} + assert.equal(parsed.length, 1) + for (const patch of parsed) { + const path = patch.newFileName.replace(/^b\//, '') + assert.ok(Object.hasOwn(expected.baselineHashes, path), `Unexpected patch path: ${path}`) + const absolute = resolve(root, path) + const current = canonicalLf(readText(absolute)) + const baseline = applyPatch(current, reversePatch(patch)) + assert.notEqual(baseline, false, `Source changed; review fix.patch: ${path}`) + const hash = (source) => createHash('sha256').update(source).digest('hex') + assert.equal(hash(baseline), expected.baselineHashes[path], `Baseline drift: ${path}`) + assert.equal(hash(current), expected.fixedHashes[path], `Fixed source drift: ${path}`) + before.set(absolute, baseline) + after.set(absolute, current) + hashes[path] = { before: hash(baseline), after: hash(current) } + } + return { root, before, after, hashes } +} + +async function load(fixed) { + const { before, after, hashes } = loadSources() + const source = (fixed ? after : before).get(resolve(root, sourcePath)) + const built = await esbuild.build({ + stdin: { + contents: + "export { SshChannelMultiplexer } from './src/main/ssh/ssh-channel-multiplexer'; export { writeToSshPtyWithSettlement } from './src/main/providers/ssh-pty-write'", + resolveDir: root, + sourcefile: 'fixture.ts', + loader: 'ts' + }, + bundle: true, + platform: 'node', + format: 'cjs', + write: false, + metafile: true, + plugins: [ + { + name: 'scheduler-variant', + setup(builder) { + builder.onLoad({ filter: /ssh-multiplexer-writer-lane-scheduler\.ts$/ }, (args) => { + assert.equal(args.path, resolve(root, sourcePath)) + return { contents: source, loader: 'ts' } + }) + } + } + ] + }) + const filename = resolve(__dirname, 'in-memory.cjs') + const expected = JSON.parse(readFileSync(resolve(__dirname, 'source-versions.json'), 'utf8')) + const dependencies = Object.keys(built.metafile.inputs) + .filter((file) => file.startsWith('src/')) + .map((file) => ({ + path: file, + sha256: sha(file === sourcePath ? source : read(resolve(root, file))) + })) + const baselineDependencies = new Map( + expected.provenance + .filter((entry) => entry.bundled) + .map((entry) => [entry.path, entry.currentBaselineSha256]) + ) + assert.equal(dependencies.length, baselineDependencies.size) + for (const dependency of dependencies) { + const expectedHash = + fixed && dependency.path === sourcePath + ? expected.fixedHashes[sourcePath] + : baselineDependencies.get(dependency.path) + assert.equal(dependency.sha256, expectedHash, `Dependency drift: ${dependency.path}`) + } + const loaded = new Module(filename, module) + loaded.filename = filename + loaded.paths = Module._nodeModulePaths(__dirname) + loaded._compile(built.outputFiles[0].text, filename) + return { + ...loaded.exports, + versions: { + sourceHashes: hashes, + bundleSha256: sha(built.outputFiles[0].contents), + dependencies + } + } +} +module.exports = { load, loadSources, canonicalLf } diff --git a/docs/audits/ssh-writer-consumed-prefix/validation.json b/docs/audits/ssh-writer-consumed-prefix/validation.json new file mode 100644 index 00000000000..613afe5f3a9 --- /dev/null +++ b/docs/audits/ssh-writer-consumed-prefix/validation.json @@ -0,0 +1,157 @@ +{ + "tests": { + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/ssh-multiplexer-writer-retention.test.ts src/main/ssh/ssh-multiplexer-transport-writer.test.ts src/main/ssh/ssh-channel-multiplexer-backpressure.test.ts src/main/ssh/ssh-channel-multiplexer-saturation-wedge.test.ts src/main/providers/ssh-pty-write.test.ts", + "passed": 31, + "files": 5, + "newRegressionCases": 5, + "exitCode": 0 + }, + "baselineOverlay": { + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/ssh-writer-consumed-prefix/before.config.mjs src/main/ssh/ssh-multiplexer-writer-retention.test.ts src/main/ssh/ssh-multiplexer-transport-writer.test.ts src/main/ssh/ssh-channel-multiplexer-backpressure.test.ts src/main/ssh/ssh-channel-multiplexer-saturation-wedge.test.ts src/main/providers/ssh-pty-write.test.ts", + "passed": 27, + "expectedFailed": 4, + "failures": "Ordinary/control rolling backlog, each with drain/dispose cleanup variant: 32 completed buffers remain reachable.", + "exitCode": 1 + }, + "typecheck": { + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm tc:node", + "exitCode": 0 + }, + "publicationQuality": { + "files": [ + "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "src/main/ssh/ssh-multiplexer-writer-retention.test.ts", + "docs/audits/ssh-writer-consumed-prefix/reproduce.cjs", + "docs/audits/ssh-writer-consumed-prefix/sources.cjs", + "docs/audits/ssh-writer-consumed-prefix/scenario.cjs", + "docs/audits/ssh-writer-consumed-prefix/before.config.mjs" + ], + "scans": [ + { + "label": "code quality", + "argv": [ + "pnpm", + "exec", + "oxlint", + "--no-ignore", + "--deny-warnings", + "--report-unused-disable-directives-severity", + "warn", + "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "src/main/ssh/ssh-multiplexer-writer-retention.test.ts", + "docs/audits/ssh-writer-consumed-prefix/reproduce.cjs", + "docs/audits/ssh-writer-consumed-prefix/sources.cjs", + "docs/audits/ssh-writer-consumed-prefix/scenario.cjs", + "docs/audits/ssh-writer-consumed-prefix/before.config.mjs" + ], + "exitCode": 0 + }, + { + "label": "casting code quality", + "argv": [ + "pnpm", + "exec", + "oxlint", + "--no-ignore", + "--deny-warnings", + "--config", + "config/oxlint-code-quality-casting.json", + "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "src/main/ssh/ssh-multiplexer-writer-retention.test.ts", + "docs/audits/ssh-writer-consumed-prefix/reproduce.cjs", + "docs/audits/ssh-writer-consumed-prefix/sources.cjs", + "docs/audits/ssh-writer-consumed-prefix/scenario.cjs", + "docs/audits/ssh-writer-consumed-prefix/before.config.mjs" + ], + "exitCode": 0 + }, + { + "label": "type-aware code quality", + "argv": [ + "pnpm", + "exec", + "oxlint", + "--no-ignore", + "--deny-warnings", + "--type-aware", + "--config", + "config/oxlint-code-quality-type-aware.json", + "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "src/main/ssh/ssh-multiplexer-writer-retention.test.ts", + "docs/audits/ssh-writer-consumed-prefix/reproduce.cjs", + "docs/audits/ssh-writer-consumed-prefix/sources.cjs", + "docs/audits/ssh-writer-consumed-prefix/scenario.cjs", + "docs/audits/ssh-writer-consumed-prefix/before.config.mjs" + ], + "exitCode": 0 + }, + { + "label": "React Doctor", + "argv": [ + "pnpm", + "exec", + "oxlint", + "--no-ignore", + "--deny-warnings", + "--config", + "config/oxlint-react-doctor.json", + "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "src/main/ssh/ssh-multiplexer-writer-retention.test.ts", + "docs/audits/ssh-writer-consumed-prefix/reproduce.cjs", + "docs/audits/ssh-writer-consumed-prefix/sources.cjs", + "docs/audits/ssh-writer-consumed-prefix/scenario.cjs", + "docs/audits/ssh-writer-consumed-prefix/before.config.mjs" + ], + "exitCode": 0 + }, + { + "label": "design system", + "argv": [ + "pnpm", + "exec", + "oxlint", + "--no-ignore", + "--deny-warnings", + "--config", + "config/oxlint-design-system.json", + "src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts", + "src/main/ssh/ssh-multiplexer-writer-retention.test.ts", + "docs/audits/ssh-writer-consumed-prefix/reproduce.cjs", + "docs/audits/ssh-writer-consumed-prefix/sources.cjs", + "docs/audits/ssh-writer-consumed-prefix/scenario.cjs", + "docs/audits/ssh-writer-consumed-prefix/before.config.mjs" + ], + "exitCode": 0 + } + ] + }, + "changedQuality": { + "command": "ORCA_BACKGROUND_LAUNCH=1 ORCA_CODE_QUALITY_BASE=HEAD pnpm run check:code-quality:changed", + "exitCode": 0, + "note": "The five explicit-file scans above include every durable CJS/MJS file; the ordinary changed gate cannot see newly ignored artifacts before staging." + }, + "proofs": { + "node": "ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/ssh-writer-consumed-prefix/reproduce.cjs", + "electron": "Installed Electron executable with ELECTRON_RUN_AS_NODE=1 and ORCA_BACKGROUND_LAUNCH=1; same flags and script path.", + "exitCodes": [0, 0], + "beforeAfterCasesPerRuntime": 12, + "crlfLoaderControl": true, + "bundledSourceHashChecks": 9 + }, + "sourceParity": { + "identicalNamedSchedulerBaselines": 3, + "independentMainIdenticalBundledAndCallerSources": 12, + "historicalIdenticalBundledAndCallerSources": 5, + "historicalProvenanceSources": 12, + "historicalExecutableReplay": false + }, + "format": "All 12 promoted TS/CJS/MJS/MD/JSON paths checked with oxfmt stdin mode, excluding fix.patch.", + "gitDiffCheckExitCode": 0, + "publicationWhitespace": { + "commandTemplate": "git diff --no-index --check ", + "files": 12, + "expectedExitCode": 1, + "diagnostics": 0, + "note": "Each complete file is checked, including ignored new artifacts. Exit 1 only reports its content differs from an empty file; no whitespace diagnostics. fix.patch uses zero-context hunks." + } +} diff --git a/src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts b/src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts index 74ad5d593b6..98a00768f5a 100644 --- a/src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts +++ b/src/main/ssh/ssh-multiplexer-writer-lane-scheduler.ts @@ -3,7 +3,7 @@ import type { MultiplexerWriterLane } from './ssh-multiplexer-transport-writer' const CONTROL_WRITES_BEFORE_ORDINARY = 4 type LaneQueue = { - entries: T[] + entries: (T | undefined)[] head: number } @@ -20,16 +20,20 @@ function shift(queue: LaneQueue): T | undefined { if (entry === undefined) { return undefined } + queue.entries[queue.head] = undefined queue.head += 1 - if (queue.head === queue.entries.length) { - queue.entries.length = 0 + if ( + queue.head === queue.entries.length || + (queue.head >= 1024 && queue.head * 2 >= queue.entries.length) + ) { + queue.entries = queue.entries.slice(queue.head) queue.head = 0 } return entry } function clear(queue: LaneQueue): T[] { - const entries = queue.entries.slice(queue.head) + const entries = queue.entries.slice(queue.head).filter((entry): entry is T => entry !== undefined) queue.entries.length = 0 queue.head = 0 return entries diff --git a/src/main/ssh/ssh-multiplexer-writer-retention.test.ts b/src/main/ssh/ssh-multiplexer-writer-retention.test.ts new file mode 100644 index 00000000000..9c74fd8c734 --- /dev/null +++ b/src/main/ssh/ssh-multiplexer-writer-retention.test.ts @@ -0,0 +1,159 @@ +import { describe, expect, it } from 'vitest' +import { + SshMultiplexerTransportWriter, + type MultiplexerTransportWriteResult, + type MultiplexerWriterLane +} from './ssh-multiplexer-transport-writer' + +async function collect(): Promise { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + for (let round = 0; round < 6; round += 1) { + await new Promise((resolve) => setImmediate(resolve)) + globalThis.gc() + } +} + +function harness() { + let drain: (() => void) | undefined + let nativeCallback: ((result: MultiplexerTransportWriteResult) => void) | undefined + const buffers: WeakRef[] = [] + const receipts: WeakRef<{ index: number }>[] = [] + const writes: number[] = [] + const settlements: { index: number; outcome: string }[] = [] + const writer = new SshMultiplexerTransportWriter( + { + supportsWriteSettlement: true, + write: (bytes, onSettled) => { + expect(nativeCallback).toBeUndefined() + nativeCallback = onSettled + writes.push(bytes.readUInt32BE()) + return false + }, + onDrain: (listener) => { + drain = listener + return () => { + drain = undefined + } + }, + onData: () => {}, + onClose: () => {} + }, + (error) => { + throw error + } + ) + return { + writer, + buffers, + receipts, + writes, + settlements, + enqueue(index: number, lane: MultiplexerWriterLane): void { + const data = Buffer.alloc(32) + data.writeUInt32BE(index) + const receipt = { index } + buffers.push(new WeakRef(data)) + receipts.push(new WeakRef(receipt)) + expect( + writer.enqueue(data, lane, (result) => { + settlements.push({ index: receipt.index, outcome: result.outcome }) + }) + ).toBe(true) + }, + drain(): void { + if (!drain) { + throw new Error('Missing drain listener') + } + drain() + }, + complete(): void { + const callback = nativeCallback + nativeCallback = undefined + if (!callback) { + throw new Error('Missing native write') + } + callback({ ok: true }) + }, + duplicateCompletion(): void { + nativeCallback?.({ ok: true }) + nativeCallback?.({ ok: false, error: new Error('Late duplicate failure') }) + } + } +} + +describe('SSH writer completed entry lifetime', () => { + for (const lane of ['ordinary', 'control'] as const) { + it.each(['drain', 'dispose'] as const)( + `releases completed ${lane} entries during a rolling backlog, then %s`, + async (release) => { + const state = harness() + try { + state.enqueue(0, lane) + state.enqueue(1, lane) + state.enqueue(2, lane) + state.complete() + for (let index = 0; index < 32; index += 1) { + state.drain() + state.complete() + state.enqueue(index + 3, lane) + } + await collect() + expect(state.buffers.slice(0, 33).filter((ref) => ref.deref())).toHaveLength(0) + expect(state.receipts.slice(0, 33).filter((ref) => ref.deref())).toHaveLength(0) + expect(state.buffers.slice(33).filter((ref) => ref.deref())).toHaveLength(2) + expect(state.receipts.slice(33).filter((ref) => ref.deref())).toHaveLength(2) + expect(state.writes).toEqual(Array.from({ length: 33 }, (_, index) => index)) + expect(state.settlements).toEqual( + state.writes.map((index) => ({ index, outcome: 'accepted' })) + ) + + if (release === 'drain') { + state.drain() + state.complete() + state.drain() + state.complete() + } else { + state.writer.dispose() + } + await collect() + expect(state.buffers.filter((ref) => ref.deref())).toHaveLength(0) + expect(state.receipts.filter((ref) => ref.deref())).toHaveLength(0) + expect(state.settlements).toHaveLength(35) + expect(state.settlements.slice(33).map((result) => result.outcome)).toEqual( + release === 'drain' ? ['accepted', 'accepted'] : ['refused', 'refused'] + ) + } finally { + state.writer.dispose() + } + } + ) + } + + it('preserves in-flight callback ownership and one settlement across disposal', async () => { + const state = harness() + try { + state.enqueue(0, 'ordinary') + state.enqueue(1, 'ordinary') + state.writer.dispose() + await collect() + expect(state.buffers[0]?.deref()).toBeDefined() + expect(state.receipts[0]?.deref()).toBeDefined() + expect(state.buffers[1]?.deref()).toBeUndefined() + expect(state.receipts[1]?.deref()).toBeUndefined() + expect(state.settlements).toEqual([ + { index: 1, outcome: 'refused' }, + { index: 0, outcome: 'unverifiable' } + ]) + state.duplicateCompletion() + state.complete() + await collect() + expect(state.buffers.filter((ref) => ref.deref())).toHaveLength(0) + expect(state.receipts.filter((ref) => ref.deref())).toHaveLength(0) + expect(state.settlements).toHaveLength(2) + } finally { + state.writer.dispose() + } + }) +}) From 1d09d557878856885b3654e96b8cbb8e570f1572 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:12:28 -0700 Subject: [PATCH 059/168] fix: fence viewport state after browser guest retirement (#21160) Co-authored-by: m4air --- .../README.md | 44 +++ .../baseline-results.json | 94 ++++++ .../baseline-source.txt | 219 ++++++++++++ .../fix.patch | 18 + .../fixed-results.json | 260 +++++++++++++++ .../source-versions.json | 98 ++++++ .../validation.json | 36 ++ .../vitest.config.mjs | 36 ++ ...browser-manager-viewport-ownership.test.ts | 312 ++++++++++++++++++ src/main/browser/browser-manager-viewport.ts | 11 +- 10 files changed, 1127 insertions(+), 1 deletion(-) create mode 100644 docs/audits/browser-viewport-owner-retention/README.md create mode 100644 docs/audits/browser-viewport-owner-retention/baseline-results.json create mode 100644 docs/audits/browser-viewport-owner-retention/baseline-source.txt create mode 100644 docs/audits/browser-viewport-owner-retention/fix.patch create mode 100644 docs/audits/browser-viewport-owner-retention/fixed-results.json create mode 100644 docs/audits/browser-viewport-owner-retention/source-versions.json create mode 100644 docs/audits/browser-viewport-owner-retention/validation.json create mode 100644 docs/audits/browser-viewport-owner-retention/vitest.config.mjs create mode 100644 src/main/browser/browser-manager-viewport-ownership.test.ts diff --git a/docs/audits/browser-viewport-owner-retention/README.md b/docs/audits/browser-viewport-owner-retention/README.md new file mode 100644 index 00000000000..bcd64e43a9a --- /dev/null +++ b/docs/audits/browser-viewport-owner-retention/README.md @@ -0,0 +1,44 @@ +# Retired browser viewport operation ownership + +The viewport operation captures a guest ID, then awaits CDP commands. Closing a tab deletes its viewport state, but the old continuation can subsequently recreate the UA-intent entry. A failed UA clear can also restore the old value over a replacement guest's completed desktop preset, or a late clear can delete the replacement's mobile intent. + +The correction reuses that captured guest ID at three mutation boundaries: before publishing an applied preset's UA intent, before reading/deleting a cleared preset's intent, and before failed-clear rollback. Same-owner rollback, native UA profiles, navigation behavior, and the per-tab promise chain are preserved. + +## Evidence + +The regression fixture calls the actual manager, registration, unregistration, and viewport implementation. Electron WebContents and pending debugger replies are controlled ports; no native browser or window is launched. + +- Baseline: **7 failing ownership regressions, 5 passing controls**. +- Fixed: **12/12 ownership cases**, plus **30 existing viewport, navigation, partial-failure, and UA cases**. +- Sixteen pending UA-clear rejections after `unregisterAll` leave **16 retired UA entries before, zero after**. Registration, preset, and promise maps remain empty. +- Other regressions cover closed-tab late success, failed-clear rollback, mobile/desktop replacement, and native-to-default profile replacement. +- Controls preserve ordinary serialized mobile/desktop/null operations, both native-profile presets, same-owner rollback, and the replacement promise tail while old queued operations settle. +- An independent reviewer ran all 12 candidate cases and reviewed the three mutation guards before promotion. + +The retained entries are tab ID strings and booleans. This does **not** demonstrate retained native WebContents, a process RSS slope, or gigabyte-scale memory growth. In-flight CDP work still owns its continuation until it settles. Positive and negative post-close command replies are injected schedules, not an affected-host trace. + +## Ordinary callers and compatibility + +The renderer requests overrides when the user selects a viewport preset and on guest `dom-ready`, including null presets. The trusted IPC handler validates dimensions before calling this manager. Navigation later reads the UA-intent map, so stale replacement values can alter the standing mobile/desktop identity. The fixture does not execute the renderer or IPC producer. + +Both local webview and host-side offscreen registrations use these maps. The correction changes no wire fields, protocol, execution-host ownership, native process lifecycle, folder/worktree handling, or UI layout. It only prevents an operation for a different guest from mutating the current registration's state. + +`source-versions.json` records 11 paths at audit checkpoint `4a09b1d1`, independent main `291b4ddd`, and reported v1.4.198 `e0826956`. The viewport implementation, registration, registry declarations, IPC handler, and toolbar producer match all three. Ten sources match independent main and eight match v1.4.198. The guest-session producer contains an earlier audit fix; historical navigation and fixture sources differ. This is a current-dependency replay with the exact historical viewport source, not a historical app-binary replay. + +The browsing activity in #19831 makes this path applicable in principle. The report does not establish the required overlap or tab count, and this small metadata mechanism does not account for its reported memory totals. + +## Reproduction + +From the worktree, run the fixed regression suite: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config docs/audits/browser-viewport-owner-retention/vitest.config.mjs +``` + +Run the same tests with the exact baseline viewport implementation; exit status 1 and seven failed cases are expected: + +```sh +ORCA_BACKGROUND_LAUNCH=1 ORCA_VIEWPORT_BASELINE=1 node node_modules/vitest/vitest.mjs run --config docs/audits/browser-viewport-owner-retention/vitest.config.mjs +``` + +The import overlay never rewrites product files. `baseline-source.txt` contains only the original viewport module; current support modules remain in use. `baseline-results.json`, `fixed-results.json`, and `validation.json` record the measured results and their scope. diff --git a/docs/audits/browser-viewport-owner-retention/baseline-results.json b/docs/audits/browser-viewport-owner-retention/baseline-results.json new file mode 100644 index 00000000000..a089d27272f --- /dev/null +++ b/docs/audits/browser-viewport-owner-retention/baseline-results.json @@ -0,0 +1,94 @@ +{ + "testFiles": 1, + "total": 12, + "passed": 5, + "failed": 7, + "cases": [ + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership does not recreate closed-tab UA intent after a late touch completion", + "status": "failed", + "failures": [ + "AssertionError: expected true to be undefined\n at ./src/main/browser/browser-manager-viewport-ownership.test.ts:109:37\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at file://./node_modules/.pnpm/@vitest+runner@4.1.11/node_modules/@vitest/runner/dist/chunk-artifact.js:1903:20" + ] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership does not restore closed-tab UA intent after a failed clear", + "status": "failed", + "failures": [ + "AssertionError: expected true to be undefined\n at ./src/main/browser/browser-manager-viewport-ownership.test.ts:126:42\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at file://./node_modules/.pnpm/@vitest+runner@4.1.11/node_modules/@vitest/runner/dist/chunk-artifact.js:1903:20" + ] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership preserves replacement desktop intent after an old clear fails", + "status": "failed", + "failures": [ + "AssertionError: expected true to be false // Object.is equality\n at ./src/main/browser/browser-manager-viewport-ownership.test.ts:142:42\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at file://./node_modules/.pnpm/@vitest+runner@4.1.11/node_modules/@vitest/runner/dist/chunk-artifact.js:1903:20" + ] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership preserves replacement mobile intent after an old clear resumes", + "status": "failed", + "failures": [ + "AssertionError: expected false to be true // Object.is equality\n at ./src/main/browser/browser-manager-viewport-ownership.test.ts:160:42\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at file://./node_modules/.pnpm/@vitest+runner@4.1.11/node_modules/@vitest/runner/dist/chunk-artifact.js:1903:20" + ] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership same-owner clear failure still restores the legitimate earlier intent", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership old apply cannot overwrite a replacement guest desktop intent", + "status": "failed", + "failures": [ + "AssertionError: expected true to be false // Object.is equality\n at ./src/main/browser/browser-manager-viewport-ownership.test.ts:185:41\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at file://./node_modules/.pnpm/@vitest+runner@4.1.11/node_modules/@vitest/runner/dist/chunk-artifact.js:1903:20" + ] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership an old native profile cannot write UA intent after replacement with a default profile", + "status": "failed", + "failures": [ + "AssertionError: expected true to be undefined\n at ./src/main/browser/browser-manager-viewport-ownership.test.ts:197:49\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at file://./node_modules/.pnpm/@vitest+runner@4.1.11/node_modules/@vitest/runner/dist/chunk-artifact.js:1903:20" + ] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership old queued operations cannot remove or join a replacement promise tail", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership normal same-owner toggles preserve last-requested order and remove the promise tail", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership native UA mode remains unchanged with mobile=false", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership native UA mode remains unchanged with mobile=true", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership late rejected clears cannot repopulate all registries after unregisterAll", + "status": "failed", + "failures": [ + "AssertionError: expected 16 to be +0 // Object.is equality\n at ./src/main/browser/browser-manager-viewport-ownership.test.ts:278:28\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at file://./node_modules/.pnpm/@vitest+runner@4.1.11/node_modules/@vitest/runner/dist/chunk-artifact.js:1903:20" + ] + } + ] +} diff --git a/docs/audits/browser-viewport-owner-retention/baseline-source.txt b/docs/audits/browser-viewport-owner-retention/baseline-source.txt new file mode 100644 index 00000000000..ce31dbe37e1 --- /dev/null +++ b/docs/audits/browser-viewport-owner-retention/baseline-source.txt @@ -0,0 +1,219 @@ +import { webContents } from 'electron' +import { + BROWSER_ANNOTATION_VIEWPORT_BRIDGE_WORLD_ID, + buildBrowserAnnotationViewportBridgeScript, + type BrowserAnnotationViewportBridgeOptions +} from '../../shared/browser-annotation-viewport-bridge' +import type { BrowserViewportOverride } from '../../shared/browser-workspace-types' +import { googleAuthUserAgent, isGoogleAuthUrl } from './browser-google-auth-ua' +import { BrowserManagerDownloadLifecycle } from './browser-manager-download-lifecycle' + +export abstract class BrowserManagerViewport extends BrowserManagerDownloadLifecycle { + // Why: guests are isolated from Orca's preload bridge, so main owns the devtools escape hatch after a tab→guest lookup. + async openDevTools(browserTabId: string): Promise { + const webContentsId = this.webContentsIdByTabId.get(browserTabId) + if (!webContentsId) { + return false + } + const guest = webContents.fromId(webContentsId) + if (!guest || guest.isDestroyed()) { + // Why: a stale guest must clear every per-tab registry entry, not just the WebContents maps. + this.unregisterGuest(browserTabId) + return false + } + // Offscreen guests have no visible window on this desktop; detaching DevTools would open it + // on the host display with no route back to the remote client. + if (this.offscreenGuestIds.has(webContentsId)) { + return false + } + guest.openDevTools({ mode: 'detach' }) + return true + } + + // Why: emulate viewport via CDP; never detach the debugger here or the agent bridge's per-guest state is cleared. + async setViewportOverride( + browserTabId: string, + override: BrowserViewportOverride | null + ): Promise { + // Why: chain per-tab so rapid toggles don't interleave CDP commands and the last-requested override wins. + const expectedWebContentsId = this.webContentsIdByTabId.get(browserTabId) + if (expectedWebContentsId !== undefined) { + // Keep host panning available while CDP applies the requested dimensions. The guest id fence + // prevents this intent from leaking to a replacement guest; clearing the preset removes it. + this.viewportPresetActiveByTabId.set(browserTabId, { + guestWebContentsId: expectedWebContentsId, + active: override !== null + }) + } + // The renderer resizes the host before CDP completes; discard the old geometry until it + // reports the new pane bounds so a pending preset cannot route wheel input using stale limits. + this.viewportScrollStateByTabId.delete(browserTabId) + const prev = this.viewportOpsByTabId.get(browserTabId) ?? Promise.resolve() + const next = prev + .catch(() => {}) + .then(() => this.doSetViewportOverrideImpl(browserTabId, override, expectedWebContentsId)) + this.viewportOpsByTabId.set(browserTabId, next) + try { + return await next + } finally { + // Why: only clear if we're still the tail; a later call may have replaced the entry, and deleting would break serialization. + if (this.viewportOpsByTabId.get(browserTabId) === next) { + this.viewportOpsByTabId.delete(browserTabId) + } + } + } + + async setAnnotationViewportBridge( + browserTabId: string, + options: BrowserAnnotationViewportBridgeOptions, + resolveGuest: () => Electron.WebContents | null + ): Promise { + const prev = this.annotationViewportBridgeOpsByTabId.get(browserTabId) ?? Promise.resolve() + const next = prev + .catch(() => {}) + .then(() => this.doSetAnnotationViewportBridgeImpl(options, resolveGuest)) + this.annotationViewportBridgeOpsByTabId.set(browserTabId, next) + try { + return await next + } finally { + if (this.annotationViewportBridgeOpsByTabId.get(browserTabId) === next) { + this.annotationViewportBridgeOpsByTabId.delete(browserTabId) + } + } + } + + // Why the caller resolves the guest: the same bridge serves browsing pages and workspace + // documents, which live in different halves of the page registry. + // Why a resolver and not the guest itself: this op may have waited behind another one, and a + // cross-process navigation meanwhile swaps the tab's contents without destroying the old one — + // injecting into the guest the request named would bridge a page nobody is looking at. + // Why no tab id: with teardown gone this reaches only the guest the resolver hands back, and + // taking an id it cannot act on would invite the next reader to act on it. + protected async doSetAnnotationViewportBridgeImpl( + options: BrowserAnnotationViewportBridgeOptions, + resolveGuest: () => Electron.WebContents | null + ): Promise { + // Why no teardown here: the resolver already unregisters a page whose guest died, and the only + // case it uniquely leaves is an ownership mismatch on a healthy page — where tearing down would + // cancel that page's in-flight downloads and grabs over a request that was merely misaddressed. + const guest = resolveGuest() + if (!guest || guest.isDestroyed()) { + return false + } + + try { + // Why: run the scroll bridge in an isolated world so page scripts can't read the per-tab token or tamper with it. + await guest.executeJavaScriptInIsolatedWorld( + BROWSER_ANNOTATION_VIEWPORT_BRIDGE_WORLD_ID, + [{ code: buildBrowserAnnotationViewportBridgeScript(options) }], + false + ) + return true + } catch { + return false + } + } + + protected async doSetViewportOverrideImpl( + browserTabId: string, + override: BrowserViewportOverride | null, + expectedWebContentsId: number | undefined + ): Promise { + const webContentsId = this.webContentsIdByTabId.get(browserTabId) + if (!webContentsId || webContentsId !== expectedWebContentsId) { + return false + } + const guest = webContents.fromId(webContentsId) + if (!guest || guest.isDestroyed()) { + // Why: a stale guest must clear every per-tab registry entry, not just the WebContents maps. + this.unregisterGuest(browserTabId) + return false + } + + try { + if (!guest.debugger.isAttached()) { + guest.debugger.attach('1.3') + } + } catch (err) { + // Why: attach throws if DevTools is open on the guest; log context so this failure mode is diagnosable. + console.warn('[browser-manager] setViewportOverride: failed to attach debugger', { + browserTabId, + webContentsId, + error: err instanceof Error ? err.message : String(err) + }) + return false + } + + const dbg = guest.debugger + try { + if (override) { + await dbg.sendCommand('Emulation.setDeviceMetricsOverride', { + width: override.width, + height: override.height, + deviceScaleFactor: override.deviceScaleFactor, + mobile: override.mobile + }) + if (this.webContentsIdByTabId.get(browserTabId) === webContentsId) { + this.viewportPresetActiveByTabId.set(browserTabId, { + guestWebContentsId: webContentsId, + active: true + }) + } + await dbg.sendCommand('Emulation.setTouchEmulationEnabled', { + enabled: override.mobile, + maxTouchPoints: override.mobile ? 5 : 0 + }) + // Why: viewport sizing must not override a profile's explicit native-UA identity. + if (this.userAgentModeByPageId.get(browserTabId) !== 'native') { + // Navigation must see the preset intent while the final CDP command is in flight. + this.viewportUaOverrideMobileByTabId.set(browserTabId, override.mobile) + // Why: same sender as the navigation path, so both resolve the tab's host identically. + await this.sendViewportUserAgentOverride(guest, override.mobile) + } + } else { + await dbg.sendCommand('Emulation.clearDeviceMetricsOverride', {}) + if (this.webContentsIdByTabId.get(browserTabId) === webContentsId) { + this.viewportPresetActiveByTabId.set(browserTabId, { + guestWebContentsId: webContentsId, + active: false + }) + } + await dbg.sendCommand('Emulation.setTouchEmulationEnabled', { + enabled: false, + maxTouchPoints: 0 + }) + const trackedMobile = this.viewportUaOverrideMobileByTabId.get(browserTabId) + // A navigation after this point must not re-install the override behind the clear. + this.viewportUaOverrideMobileByTabId.delete(browserTabId) + try { + if (this.authUserAgentOverrideStateByGuestId.has(guest.id)) { + const url = this.resolveTabNavigationUrl(guest) + const restored = await this.applyAuthUserAgentOverrideOverCdp( + guest, + false, + url, + isGoogleAuthUrl(url) ? googleAuthUserAgent() : guest.session.getUserAgent() + ) + if (!restored) { + throw new Error('Failed to preserve auth user agent') + } + } else { + // Why: passing an empty string restores the session default UA. + await dbg.sendCommand('Emulation.setUserAgentOverride', { userAgent: '' }) + } + } catch (error) { + if (trackedMobile !== undefined) { + this.viewportUaOverrideMobileByTabId.set(browserTabId, trackedMobile) + } + throw error + } + } + if (this.webContentsIdByTabId.get(browserTabId) !== webContentsId) { + return false + } + return true + } catch { + return false + } + } +} diff --git a/docs/audits/browser-viewport-owner-retention/fix.patch b/docs/audits/browser-viewport-owner-retention/fix.patch new file mode 100644 index 00000000000..be47f36792b --- /dev/null +++ b/docs/audits/browser-viewport-owner-retention/fix.patch @@ -0,0 +1,18 @@ +diff --git a/src/main/browser/browser-manager-viewport.ts b/src/main/browser/browser-manager-viewport.ts +index ce31dbe37e..3f1fbb68fb 100644 +--- a/src/main/browser/browser-manager-viewport.ts ++++ b/src/main/browser/browser-manager-viewport.ts +@@ -165,0 +166,3 @@ export abstract class BrowserManagerViewport extends BrowserManagerDownloadLifec ++ if (this.webContentsIdByTabId.get(browserTabId) !== webContentsId) { ++ return false ++ } +@@ -184,0 +188,3 @@ export abstract class BrowserManagerViewport extends BrowserManagerDownloadLifec ++ if (this.webContentsIdByTabId.get(browserTabId) !== webContentsId) { ++ return false ++ } +@@ -205 +211,4 @@ export abstract class BrowserManagerViewport extends BrowserManagerDownloadLifec +- if (trackedMobile !== undefined) { ++ if ( ++ trackedMobile !== undefined && ++ this.webContentsIdByTabId.get(browserTabId) === webContentsId ++ ) { diff --git a/docs/audits/browser-viewport-owner-retention/fixed-results.json b/docs/audits/browser-viewport-owner-retention/fixed-results.json new file mode 100644 index 00000000000..3567f8d3eff --- /dev/null +++ b/docs/audits/browser-viewport-owner-retention/fixed-results.json @@ -0,0 +1,260 @@ +{ + "testFiles": 4, + "total": 42, + "passed": 42, + "failed": 0, + "cases": [ + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride returns false when the tab is not registered", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride applies device metrics, touch emulation, and a mobile UA for mobile presets", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride keeps the session UA for native-mode profiles when mobile=false", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride keeps the session UA for native-mode profiles when mobile=true", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride presents the Firefox UA for a preset applied on a Google auth host (mobile=false)", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride presents the Firefox UA for a preset applied on a Google auth host (mobile=true)", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride re-issues the standing UA override when navigating onto and back off an auth host", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride does not leave the Chrome preset UA standing when a mobile preset lands mid-navigation onto an auth host", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride does not leave the Firefox UA standing when a preset lands mid-navigation off an auth host", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride falls back to the committed URL once a navigation commits or fails", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride does not let a superseded navigation failure revert a newer target", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride switches identity for a server redirect and restores it if the redirect fails", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride preserves the auth identity when a viewport preset is cleared after a redirect", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride does not inherit a mobile owner UA in a desktop popup", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride reapplies a preset when navigation starts during its final UA write", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride does not reinstall a preset while its final UA clear is in flight", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride keeps tracking the standing override when the CDP clear fails", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride does not touch the UA override on navigation when no preset is standing", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride stops re-issuing the UA override once the preset is cleared", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride leaves the UA override alone on navigation for native-UA profiles", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride clears device metrics and disables touch for override=null", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride attaches the debugger if not already attached and does not detach after", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-override.test.ts", + "title": "browserManager setViewportOverride returns false when debugger.attach throws (e.g. DevTools already open)", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership does not recreate closed-tab UA intent after a late touch completion", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership does not restore closed-tab UA intent after a failed clear", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership preserves replacement desktop intent after an old clear fails", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership preserves replacement mobile intent after an old clear resumes", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership same-owner clear failure still restores the legitimate earlier intent", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership old apply cannot overwrite a replacement guest desktop intent", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership an old native profile cannot write UA intent after replacement with a default profile", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership old queued operations cannot remove or join a replacement promise tail", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership normal same-owner toggles preserve last-requested order and remove the promise tail", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership native UA mode remains unchanged with mobile=false", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership native UA mode remains unchanged with mobile=true", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-ownership.test.ts", + "title": "browser viewport operation ownership late rejected clears cannot repopulate all registries after unregisterAll", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-partial-failure.test.ts", + "title": "browserManager viewport partial failure keeps wheel routing active when follow-up setup fails after metrics apply", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-manager-viewport-partial-failure.test.ts", + "title": "browserManager viewport partial failure keeps host panning available when metrics setup fails", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-viewport-user-agent.test.ts", + "title": "buildViewportUserAgentOverride presents the Firefox UA on Google auth hosts regardless of the preset", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-viewport-user-agent.test.ts", + "title": "buildViewportUserAgentOverride keeps the clean desktop UA off the auth hosts", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-viewport-user-agent.test.ts", + "title": "buildViewportUserAgentOverride splices the real Chrome major into the mobile UA and its client hints", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-viewport-user-agent.test.ts", + "title": "buildViewportUserAgentOverride falls back to a known Chrome major when the base UA carries none", + "status": "passed", + "failures": [] + }, + { + "file": "src/main/browser/browser-viewport-user-agent.test.ts", + "title": "buildViewportUserAgentOverride treats an unparseable URL as a non-auth host", + "status": "passed", + "failures": [] + } + ] +} diff --git a/docs/audits/browser-viewport-owner-retention/source-versions.json b/docs/audits/browser-viewport-owner-retention/source-versions.json new file mode 100644 index 00000000000..dabc3a96c01 --- /dev/null +++ b/docs/audits/browser-viewport-owner-retention/source-versions.json @@ -0,0 +1,98 @@ +{ + "refs": { + "audit": "4a09b1d108cfd8b57ffcc5d727b3a3bd71ae71fb", + "main": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "v1.4.198": "e0826956fcfc532f5a1e55b5e081f2e57e553c43" + }, + "canonicalLineEndings": "LF", + "sources": [ + { + "path": "src/main/browser/browser-manager-viewport.ts", + "sha256": { + "audit": "701f9ea1a4310e509f409e08ee4ea0539f820bd209ff68f41001b8c91e5470e4", + "main": "701f9ea1a4310e509f409e08ee4ea0539f820bd209ff68f41001b8c91e5470e4", + "v1.4.198": "701f9ea1a4310e509f409e08ee4ea0539f820bd209ff68f41001b8c91e5470e4" + } + }, + { + "path": "src/main/browser/browser-manager-registration.ts", + "sha256": { + "audit": "12c149b1fb87b67b4192b6c2f23bd7f45b7df2d0c60368182f33b5a47b56f96f", + "main": "12c149b1fb87b67b4192b6c2f23bd7f45b7df2d0c60368182f33b5a47b56f96f", + "v1.4.198": "12c149b1fb87b67b4192b6c2f23bd7f45b7df2d0c60368182f33b5a47b56f96f" + } + }, + { + "path": "src/main/browser/browser-manager-navigation.ts", + "sha256": { + "audit": "a4c0e169ac35725b02d050c95843721e4274775a5bcb9ef2a5b7c835c4d8c234", + "main": "a4c0e169ac35725b02d050c95843721e4274775a5bcb9ef2a5b7c835c4d8c234", + "v1.4.198": "c93c060896351b4bc23db628a732ef4db4acd5b26760e4565e6bf029d5cf7531" + } + }, + { + "path": "src/main/browser/browser-manager-guest-policy.ts", + "sha256": { + "audit": "6d567b0c675091ec0a59d39ffe701fd36098ec2730b61ef0b80ab53962da6144", + "main": "6d567b0c675091ec0a59d39ffe701fd36098ec2730b61ef0b80ab53962da6144", + "v1.4.198": "6d567b0c675091ec0a59d39ffe701fd36098ec2730b61ef0b80ab53962da6144" + } + }, + { + "path": "src/main/browser/browser-manager-state.ts", + "sha256": { + "audit": "ea6e847afe227b9acbf432d68a6f21f0d15247748fc8b4f769ab73498d6a13c5", + "main": "ea6e847afe227b9acbf432d68a6f21f0d15247748fc8b4f769ab73498d6a13c5", + "v1.4.198": "ea6e847afe227b9acbf432d68a6f21f0d15247748fc8b4f769ab73498d6a13c5" + } + }, + { + "path": "src/main/browser/browser-manager-types.ts", + "sha256": { + "audit": "4d28f7c397aa82b067971ff769a44fff74adadceaac66774345a615f799bb64f", + "main": "4d28f7c397aa82b067971ff769a44fff74adadceaac66774345a615f799bb64f", + "v1.4.198": "4d28f7c397aa82b067971ff769a44fff74adadceaac66774345a615f799bb64f" + } + }, + { + "path": "src/main/browser/browser-manager-viewport-test-fixtures.ts", + "sha256": { + "audit": "0ef61054ae131db056b5268c6ed4f417b4486784d58d2c0d7d2285ad785666ee", + "main": "0ef61054ae131db056b5268c6ed4f417b4486784d58d2c0d7d2285ad785666ee", + "v1.4.198": "36d29f1d78bd559af3b235acc9be8dafe763e3dfb9ea4367272db04449eca707" + } + }, + { + "path": "src/main/browser/browser-manager-test-harness.ts", + "sha256": { + "audit": "66de235be9285ec2cd2a6d783c8e8a046d6106d4171aaf808b5d263c08a72038", + "main": "66de235be9285ec2cd2a6d783c8e8a046d6106d4171aaf808b5d263c08a72038", + "v1.4.198": "66de235be9285ec2cd2a6d783c8e8a046d6106d4171aaf808b5d263c08a72038" + } + }, + { + "path": "src/main/ipc/browser-guest-view-ipc.ts", + "sha256": { + "audit": "da4a441ce5f0851f1a1c6ec38c872c187ec191a9acad340f05ac557534731959", + "main": "da4a441ce5f0851f1a1c6ec38c872c187ec191a9acad340f05ac557534731959", + "v1.4.198": "da4a441ce5f0851f1a1c6ec38c872c187ec191a9acad340f05ac557534731959" + } + }, + { + "path": "src/renderer/src/components/browser-pane/host-guest/browser-page-webview-guest-session.ts", + "sha256": { + "audit": "a78cc98873e93834bf07b47bbf5d92da888dfbccef06551aa6ac3c8f6e9f29f8", + "main": "2fe17f0fe4f8ef6ca7cff7ca5731d9d725dbf4b5e7944264e30f12241317fc6d", + "v1.4.198": "6c089c0c8285b21b3f3c0b3e06bd297a25d941cfa8a4849d03fbd08a6c89c139" + } + }, + { + "path": "src/renderer/src/components/browser-pane/assemble-chrome/BrowserToolbarMenu.tsx", + "sha256": { + "audit": "e05e7d8d532d2f43f13a8dc1035540638ab513c9f3d6bdbd1531cf3e0e9462e1", + "main": "e05e7d8d532d2f43f13a8dc1035540638ab513c9f3d6bdbd1531cf3e0e9462e1", + "v1.4.198": "e05e7d8d532d2f43f13a8dc1035540638ab513c9f3d6bdbd1531cf3e0e9462e1" + } + } + ] +} diff --git a/docs/audits/browser-viewport-owner-retention/validation.json b/docs/audits/browser-viewport-owner-retention/validation.json new file mode 100644 index 00000000000..2a7de53af18 --- /dev/null +++ b/docs/audits/browser-viewport-owner-retention/validation.json @@ -0,0 +1,36 @@ +{ + "scope": "Actual manager and lifecycle; controlled Electron/CDP ports; no native guest, heap/RSS or incident attribution", + "baseline": { + "total": 12, + "failedOwnershipCases": 7, + "passedControls": 5 + }, + "fixed": { + "total": 42, + "passed": 42, + "testFiles": 4 + }, + "independentReview": { + "candidateTestsPassed": 12, + "findings": "No blocker; three map mutation guards preserve current guest and promise ownership" + }, + "typecheck": { + "node": "passed after correcting fixture-only protected-map reads and array typing", + "cli": "passed", + "web": "passed" + }, + "quality": { + "fullFileScans": 5, + "codeFiles": 3, + "newDiagnostics": 0 + }, + "sourceSha256": { + "src/main/browser/browser-manager-viewport.ts": "a839fd89cc5e687782323036ca3a8dd9de79bd838e77dd863a5e1ae101424b4c", + "src/main/browser/browser-manager-viewport-ownership.test.ts": "ec17f2acd6b766166d13cafc2ecd33f1d4f0de7a4b4e8896d0746b2d528341da" + }, + "limitations": [ + "Pending CDP response schedules are injected, not an affected-host capture", + "Retired map values are booleans; native objects and process RSS were not measured", + "Historical viewport source is exact; surrounding dependencies execute current audit versions" + ] +} diff --git a/docs/audits/browser-viewport-owner-retention/vitest.config.mjs b/docs/audits/browser-viewport-owner-retention/vitest.config.mjs new file mode 100644 index 00000000000..37cec186182 --- /dev/null +++ b/docs/audits/browser-viewport-owner-retention/vitest.config.mjs @@ -0,0 +1,36 @@ +import { readFileSync } from 'node:fs' +import { fileURLToPath } from 'node:url' +import base from '../../../config/vitest.config.ts' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Set ORCA_BACKGROUND_LAUNCH=1 for the viewport ownership replay') +} + +const target = fileURLToPath( + new URL('../../../src/main/browser/browser-manager-viewport.ts', import.meta.url) +).replaceAll('\\', '/') + +export default { + ...base, + test: { + ...base.test, + include: ['src/main/browser/browser-manager-viewport-ownership.test.ts'] + }, + plugins: + process.env.ORCA_VIEWPORT_BASELINE === '1' + ? [ + { + name: 'viewport-owner-baseline', + enforce: 'pre', + transform(_source, id) { + return id.replaceAll('\\', '/').split('?')[0] === target + ? { + code: readFileSync(new URL('./baseline-source.txt', import.meta.url), 'utf8'), + map: null + } + : null + } + } + ] + : [] +} diff --git a/src/main/browser/browser-manager-viewport-ownership.test.ts b/src/main/browser/browser-manager-viewport-ownership.test.ts new file mode 100644 index 00000000000..8b4492f36fd --- /dev/null +++ b/src/main/browser/browser-manager-viewport-ownership.test.ts @@ -0,0 +1,312 @@ +import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest' + +const mocks = vi.hoisted(() => ({ + appGetPathMock: vi.fn(() => '/downloads'), + shellOpenExternalMock: vi.fn(), + browserWindowFromWebContentsMock: vi.fn(), + menuBuildFromTemplateMock: vi.fn(), + guestOffMock: vi.fn(), + guestOnMock: vi.fn(), + guestSetBackgroundThrottlingMock: vi.fn(), + guestSetWindowOpenHandlerMock: vi.fn(), + guestOpenDevToolsMock: vi.fn(), + webContentsFromIdMock: vi.fn(), + screenGetCursorScreenPointMock: vi.fn(() => ({ x: 0, y: 0 })), + openPopupWithOriginBarMock: vi.fn(), + processUserAgentMode: 'clean', + processUserAgent: '' +})) + +vi.mock('electron', () => ({ + app: { getPath: mocks.appGetPathMock }, + BrowserWindow: { fromWebContents: mocks.browserWindowFromWebContentsMock }, + clipboard: { writeText: vi.fn() }, + shell: { openExternal: mocks.shellOpenExternalMock }, + Menu: { buildFromTemplate: mocks.menuBuildFromTemplateMock }, + screen: { getCursorScreenPoint: mocks.screenGetCursorScreenPointMock }, + webContents: { fromId: mocks.webContentsFromIdMock } +})) +vi.mock('./popup-origin-bar-window', () => ({ + openPopupWithOriginBar: mocks.openPopupWithOriginBarMock +})) + +vi.mock('./browser-process-user-agent', () => ({ + getBrowserProcessUserAgentIdentity: () => ({ + mode: mocks.processUserAgentMode, + userAgent: mocks.processUserAgent + }) +})) + +import { browserManager } from './browser-manager' +import { resetBrowserManagerMocks, resetBrowserManagerState } from './browser-manager-test-harness' +import { + createViewportGuestFactory, + GUEST_CLEAN_UA, + GUEST_ELECTRON_UA +} from './browser-manager-viewport-test-fixtures' + +const makeGuest = createViewportGuestFactory(mocks) +const mobile = { width: 375, height: 667, deviceScaleFactor: 2, mobile: true } +const desktop = { width: 1440, height: 900, deviceScaleFactor: 1, mobile: false } +const guests = new Map>() +const registeredGuests = readViewportStateMap('webContentsIdByTabId') +const uaIntents = readViewportStateMap('viewportUaOverrideMobileByTabId') +const presetIntents = readViewportStateMap('viewportPresetActiveByTabId') +const pendingOperations = readViewportStateMap('viewportOpsByTabId') + +function readViewportStateMap( + name: + | 'webContentsIdByTabId' + | 'viewportUaOverrideMobileByTabId' + | 'viewportPresetActiveByTabId' + | 'viewportOpsByTabId' +): Map { + const value: unknown = browserManager[name] + if (!(value instanceof Map)) { + throw new Error(`Expected manager state map: ${name}`) + } + return value +} + +function register(tab: string, id: number) { + const handle = makeGuest(id) + guests.set(id, handle.guest) + expect( + browserManager.registerOffscreenGuest({ + browserPageId: tab, + webContentsId: id + }) + ).toBe(true) + return handle +} + +function pause(handle: ReturnType, method: string) { + const entered = Promise.withResolvers() + const gate = Promise.withResolvers() + let blocked = false + handle.debuggerSendCommand.mockImplementation((next) => { + if (!blocked && next === method) { + blocked = true + entered.resolve() + return gate.promise + } + return Promise.resolve() + }) + return { entered: entered.promise, ...gate } +} + +describe('browser viewport operation ownership', () => { + beforeEach(() => { + expect(process.env.ORCA_BACKGROUND_LAUNCH).toBe('1') + resetBrowserManagerMocks(mocks) + resetBrowserManagerState() + mocks.processUserAgentMode = 'clean' + mocks.processUserAgent = GUEST_CLEAN_UA + guests.clear() + mocks.webContentsFromIdMock.mockImplementation((id) => guests.get(id)) + }) + afterEach(() => { + browserManager.unregisterAll() + vi.restoreAllMocks() + }) + + it('does not recreate closed-tab UA intent after a late touch completion', async () => { + const handle = register('closed', 100) + const gate = pause(handle, 'Emulation.setTouchEmulationEnabled') + const result = browserManager.setViewportOverride('closed', mobile) + await gate.entered + browserManager.unregisterGuest('closed') + expect(uaIntents.size).toBe(0) + const isDestroyed = handle.guest.isDestroyed + expect(vi.isMockFunction(isDestroyed)).toBe(true) + if (vi.isMockFunction(isDestroyed)) { + isDestroyed.mockReturnValue(true) + } + gate.resolve() + await expect(result).resolves.toBe(false) + expect(registeredGuests.size).toBe(0) + expect(presetIntents.size).toBe(0) + expect(uaIntents.get('closed')).toBeUndefined() + }) + + it('does not restore closed-tab UA intent after a failed clear', async () => { + const handle = register('clear-close', 101) + await expect(browserManager.setViewportOverride('clear-close', mobile)).resolves.toBe(true) + const gate = pause(handle, 'Emulation.setUserAgentOverride') + const result = browserManager.setViewportOverride('clear-close', null) + await gate.entered + browserManager.unregisterGuest('clear-close') + const isDestroyed = handle.guest.isDestroyed + expect(vi.isMockFunction(isDestroyed)).toBe(true) + if (vi.isMockFunction(isDestroyed)) { + isDestroyed.mockReturnValue(true) + } + gate.reject(new Error('Target closed')) + await expect(result).resolves.toBe(false) + expect(uaIntents.get('clear-close')).toBeUndefined() + }) + + it('preserves replacement desktop intent after an old clear fails', async () => { + const handle = register('replacement', 102) + await browserManager.setViewportOverride('replacement', mobile) + const gate = pause(handle, 'Emulation.setUserAgentOverride') + const result = browserManager.setViewportOverride('replacement', null) + await gate.entered + browserManager.unregisterGuest('replacement') + register('replacement', 103) + await expect(browserManager.setViewportOverride('replacement', desktop)).resolves.toBe(true) + expect(uaIntents.get('replacement')).toBe(false) + gate.reject(new Error('Old target closed')) + await expect(result).resolves.toBe(false) + expect(registeredGuests.get('replacement')).toBe(103) + expect(uaIntents.get('replacement')).toBe(false) + expect(presetIntents.get('replacement')).toEqual({ + guestWebContentsId: 103, + active: true + }) + }) + + it('preserves replacement mobile intent after an old clear resumes', async () => { + const handle = register('late-delete', 104) + await browserManager.setViewportOverride('late-delete', desktop) + const gate = pause(handle, 'Emulation.setTouchEmulationEnabled') + const result = browserManager.setViewportOverride('late-delete', null) + await gate.entered + browserManager.unregisterGuest('late-delete') + register('late-delete', 105) + await browserManager.setViewportOverride('late-delete', mobile) + gate.resolve() + await expect(result).resolves.toBe(false) + expect(uaIntents.has('late-delete')).toBe(true) + expect(registeredGuests.get('late-delete')).toBe(105) + }) + + it('same-owner clear failure still restores the legitimate earlier intent', async () => { + const handle = register('same-owner', 106) + await browserManager.setViewportOverride('same-owner', mobile) + const gate = pause(handle, 'Emulation.setUserAgentOverride') + const result = browserManager.setViewportOverride('same-owner', null) + await gate.entered + gate.reject(new Error('Protocol error')) + await expect(result).resolves.toBe(false) + expect(uaIntents.get('same-owner')).toBe(true) + }) + + it('old apply cannot overwrite a replacement guest desktop intent', async () => { + const old = register('late-apply', 110) + const gate = pause(old, 'Emulation.setTouchEmulationEnabled') + const pending = browserManager.setViewportOverride('late-apply', mobile) + await gate.entered + browserManager.unregisterGuest('late-apply') + register('late-apply', 111) + await browserManager.setViewportOverride('late-apply', desktop) + gate.resolve() + await expect(pending).resolves.toBe(false) + expect(uaIntents.get('late-apply')).toBe(false) + }) + + it('an old guest cannot write UA intent after replacement in native process mode', async () => { + mocks.processUserAgentMode = 'native' + mocks.processUserAgent = GUEST_ELECTRON_UA + const old = register('native-replacement', 112) + const gate = pause(old, 'Emulation.setTouchEmulationEnabled') + const pending = browserManager.setViewportOverride('native-replacement', mobile) + await gate.entered + browserManager.unregisterGuest('native-replacement') + register('native-replacement', 113) + gate.resolve() + await expect(pending).resolves.toBe(false) + expect(uaIntents.get('native-replacement')).toBeUndefined() + }) + + it('old queued operations cannot remove or join a replacement promise tail', async () => { + const old = register('queued-replacement', 114) + const oldGate = pause(old, 'Emulation.setTouchEmulationEnabled') + const first = browserManager.setViewportOverride('queued-replacement', mobile) + const second = browserManager.setViewportOverride('queued-replacement', null) + await oldGate.entered + browserManager.unregisterGuest('queued-replacement') + const replacement = register('queued-replacement', 115) + const newGate = pause(replacement, 'Emulation.setTouchEmulationEnabled') + const replacementFirst = browserManager.setViewportOverride('queued-replacement', desktop) + const replacementSecond = browserManager.setViewportOverride('queued-replacement', mobile) + await newGate.entered + const tail = pendingOperations.get('queued-replacement') + oldGate.resolve() + await expect(first).resolves.toBe(false) + await expect(second).resolves.toBe(false) + expect(pendingOperations.get('queued-replacement')).toBe(tail) + newGate.resolve() + await expect(replacementFirst).resolves.toBe(true) + await expect(replacementSecond).resolves.toBe(true) + expect(pendingOperations.size).toBe(0) + expect(uaIntents.get('queued-replacement')).toBe(true) + }) + + it('normal same-owner toggles preserve last-requested order and remove the promise tail', async () => { + const handle = register('serialized', 116) + const gate = pause(handle, 'Emulation.setTouchEmulationEnabled') + const first = browserManager.setViewportOverride('serialized', mobile) + await gate.entered + const second = browserManager.setViewportOverride('serialized', desktop) + const third = browserManager.setViewportOverride('serialized', null) + gate.resolve() + expect(await Promise.all([first, second, third])).toEqual([true, true, true]) + expect(handle.debuggerSendCommand.mock.calls.map(([method]) => method)).toEqual([ + 'Emulation.setDeviceMetricsOverride', + 'Emulation.setTouchEmulationEnabled', + 'Emulation.setUserAgentOverride', + 'Emulation.setDeviceMetricsOverride', + 'Emulation.setTouchEmulationEnabled', + 'Emulation.setUserAgentOverride', + 'Emulation.clearDeviceMetricsOverride', + 'Emulation.setTouchEmulationEnabled', + 'Emulation.setUserAgentOverride' + ]) + expect(pendingOperations.size).toBe(0) + expect(uaIntents.size).toBe(0) + }) + + it.each([false, true])( + 'keeps process-wide native UA behavior with mobile=%s', + async (mobileMode) => { + mocks.processUserAgentMode = 'native' + mocks.processUserAgent = GUEST_ELECTRON_UA + const handle = register('native', 117) + await expect( + browserManager.setViewportOverride('native', mobileMode ? mobile : desktop) + ).resolves.toBe(true) + expect(handle.debuggerSendCommand).toHaveBeenCalledWith( + 'Emulation.setUserAgentOverride', + mobileMode + ? expect.objectContaining({ userAgent: expect.stringContaining('iPhone') }) + : { userAgent: GUEST_ELECTRON_UA } + ) + expect(uaIntents.get('native')).toBe(mobileMode) + } + ) + + it('late rejected clears cannot repopulate all registries after unregisterAll', async () => { + const operations: { gate: ReturnType; pending: Promise }[] = [] + for (let index = 0; index < 16; index++) { + const tab = `all-closed-${index}` + const handle = register(tab, 200 + index) + await browserManager.setViewportOverride(tab, mobile) + const gate = pause(handle, 'Emulation.setUserAgentOverride') + const pending = browserManager.setViewportOverride(tab, null) + await gate.entered + operations.push({ gate, pending }) + } + browserManager.unregisterAll() + for (const { gate } of operations) { + gate.reject(new Error('Target closed')) + } + expect(await Promise.all(operations.map(({ pending }) => pending))).toEqual( + Array(16).fill(false) + ) + expect(uaIntents.size).toBe(0) + expect(registeredGuests.size).toBe(0) + expect(pendingOperations.size).toBe(0) + expect(presetIntents.size).toBe(0) + }) +}) diff --git a/src/main/browser/browser-manager-viewport.ts b/src/main/browser/browser-manager-viewport.ts index b5599ab4760..a4e263dcced 100644 --- a/src/main/browser/browser-manager-viewport.ts +++ b/src/main/browser/browser-manager-viewport.ts @@ -164,6 +164,9 @@ export abstract class BrowserManagerViewport extends BrowserManagerDownloadLifec enabled: override.mobile, maxTouchPoints: override.mobile ? 5 : 0 }) + if (this.webContentsIdByTabId.get(browserTabId) !== webContentsId) { + return false + } // Navigation must see the preset while the final CDP write is in flight. this.viewportUaOverrideMobileByTabId.set(browserTabId, override.mobile) await this.sendViewportUserAgentOverride(guest, override.mobile) @@ -179,6 +182,9 @@ export abstract class BrowserManagerViewport extends BrowserManagerDownloadLifec enabled: false, maxTouchPoints: 0 }) + if (this.webContentsIdByTabId.get(browserTabId) !== webContentsId) { + return false + } const trackedMobile = this.viewportUaOverrideMobileByTabId.get(browserTabId) // A navigation after this point must not re-install the override behind the clear. this.viewportUaOverrideMobileByTabId.delete(browserTabId) @@ -204,7 +210,10 @@ export abstract class BrowserManagerViewport extends BrowserManagerDownloadLifec await dbg.sendCommand('Emulation.setUserAgentOverride', { userAgent: '' }) } } catch (error) { - if (trackedMobile !== undefined) { + if ( + trackedMobile !== undefined && + this.webContentsIdByTabId.get(browserTabId) === webContentsId + ) { this.viewportUaOverrideMobileByTabId.set(browserTabId, trackedMobile) } throw error From 98998b18ad2fe89f1a07dfe76788d73c96ec93f5 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:12:31 -0700 Subject: [PATCH 060/168] fix: release retired shared daemon owner metadata (#21162) Co-authored-by: m4air --- .../README.md | 67 + .../before.config.mjs | 23 + .../electron-results.json | 1582 +++++++++++++++++ .../fix.patch | 11 + .../node-results.json | 1581 ++++++++++++++++ .../publication-electron-results.json | 1574 ++++++++++++++++ .../publication-node-results.json | 1573 ++++++++++++++++ .../reproduce.cjs | 61 + .../scenario.cjs | 182 ++ .../source-versions.json | 1407 +++++++++++++++ .../sources.cjs | 100 ++ .../validation.json | 88 + .../daemon/daemon-session-owner-resolution.ts | 6 + ...shared-owner-incarnation-retention.test.ts | 157 ++ 14 files changed, 8412 insertions(+) create mode 100644 docs/audits/daemon-shared-owner-incarnation-retention/README.md create mode 100644 docs/audits/daemon-shared-owner-incarnation-retention/before.config.mjs create mode 100644 docs/audits/daemon-shared-owner-incarnation-retention/electron-results.json create mode 100644 docs/audits/daemon-shared-owner-incarnation-retention/fix.patch create mode 100644 docs/audits/daemon-shared-owner-incarnation-retention/node-results.json create mode 100644 docs/audits/daemon-shared-owner-incarnation-retention/publication-electron-results.json create mode 100644 docs/audits/daemon-shared-owner-incarnation-retention/publication-node-results.json create mode 100644 docs/audits/daemon-shared-owner-incarnation-retention/reproduce.cjs create mode 100644 docs/audits/daemon-shared-owner-incarnation-retention/scenario.cjs create mode 100644 docs/audits/daemon-shared-owner-incarnation-retention/source-versions.json create mode 100644 docs/audits/daemon-shared-owner-incarnation-retention/sources.cjs create mode 100644 docs/audits/daemon-shared-owner-incarnation-retention/validation.json create mode 100644 src/main/daemon/daemon-shared-owner-incarnation-retention.test.ts diff --git a/docs/audits/daemon-shared-owner-incarnation-retention/README.md b/docs/audits/daemon-shared-owner-incarnation-retention/README.md new file mode 100644 index 00000000000..9ed1bf5008a --- /dev/null +++ b/docs/audits/daemon-shared-owner-incarnation-retention/README.md @@ -0,0 +1,67 @@ +# Shared daemon owner incarnation retention + +A degraded daemon provider creates two owner resolvers with one shared route map. On an authenticated daemon identity change, the attach resolver removes that daemon’s routes first. The liveness resolver then sees no corresponding routes and previously left its private session-to-incarnation entries behind. Repeating replacements with newly discovered session IDs grows that private map for the lifetime of the degraded provider. + +The fix removes private incarnation entries whose shared route is absent after provider invalidation. It preserves every remaining route, including another provider’s live session and a same-ID successor. It does not change process liveness, stop remote work, change the wire protocol, or depend on a git workspace. + +## Actual ownership and trigger + +- `src/main/daemon/daemon-provider-init.ts:123` selects `DegradedDaemonPtyProvider` for `degraded-new-pty-fallback`; startup discovery runs at line 139. +- `src/main/daemon/degraded-daemon-owner-recovery.ts:15` constructs both resolvers with the same map; public discovery and liveness probes populate their private indexes. Startup reconciliation can also record both routes. +- `src/main/daemon/degraded-daemon-owner-recovery.ts:70` subscribes to each daemon’s identity publication and invalidates the attach resolver before the liveness resolver. +- `src/main/daemon/daemon-pty-connection-lifecycle.ts:41` publishes only after a different authenticated identity replaces a previous identity. Repeated observation of the same identity does not retire anything. +- `src/main/daemon/daemon-pty-daemon-recovery.ts:268` can replace the daemon while retaining its adapter and the degraded provider. +- `src/main/daemon/daemon-session-owner-resolution.ts:44` performs the invalidation and the new private-metadata prune. + +This is a local desktop main-process degraded-provider path. Loss of SSH contact is not its retirement trigger. Entry counts below do not establish retained bytes, RSS, an OOM, or causation for #19831. + +## Bounded actual-source proof + +The fixture uses the actual degraded provider, recovery controller, resolvers, daemon adapter inventory, authenticated identity publication, and direct attach implementation. Only finite authenticated transport replies and the empty fallback provider are inert; it starts no native PTY, socket, network connection, or application window. It does not depend on garbage-collection timing or a never-settling promise. + +Each of 32 cycles discovers a new current-daemon session, populates both resolvers through public calls, observes an unchanged identity, then publishes a replacement identity. An unrelated legacy-daemon session remains live throughout. Finally, an ordinary legacy exit removes its route from both resolvers. + +| After 32 replacements and the legacy exit | Baseline | Fixed | +| ----------------------------------------- | -------: | ----: | +| Shared routes | 0 | 0 | +| Attach resolver incarnation entries | 0 | 0 | +| Liveness resolver incarnation entries | 32 | 0 | + +Additional assertions preserve a same-ID successor on another provider, an unchanged authenticated identity, direct attach with a matching authoritative incarnation without inventory, refusal of a mismatched authoritative incarnation, and ordinary exit cleanup. The permanent tests include the repetition regression and three compatibility controls; the portable fixture additionally exercises the actual adapter attach transport path. + +## Reproduce + +Run from the repository root with its dependencies installed: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node docs/audits/daemon-shared-owner-incarnation-retention/reproduce.cjs +ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/daemon/daemon-shared-owner-incarnation-retention.test.ts src/main/daemon/daemon-session-owner-resolution.test.ts src/main/daemon/degraded-daemon-pty-provider.test.ts +``` + +The runner accepts an optional output filename as its first argument. On macOS, the Electron runtime control is: + +```sh +ORCA_BACKGROUND_LAUNCH=1 ELECTRON_RUN_AS_NODE=1 node_modules/electron/dist/Electron.app/Contents/MacOS/Electron docs/audits/daemon-shared-owner-incarnation-retention/reproduce.cjs +``` + +On Linux or Windows, use the corresponding installed Electron binary with the same environment variables. It runs as Node and never displays a window. + +The baseline test overlay reverses only the fenced product patch in memory: + +```sh +ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/daemon-shared-owner-incarnation-retention/before.config.mjs src/main/daemon/daemon-shared-owner-incarnation-retention.test.ts src/main/daemon/daemon-session-owner-resolution.test.ts src/main/daemon/degraded-daemon-pty-provider.test.ts +``` + +Expected: exactly the new repeated-retirement assertion fails before the fix; the other 53 tests pass. All 54 pass with the fix. + +## Source identities and publication independence + +`sources.cjs` checks the exact fixed source hash, reverses `fix.patch`, checks the baseline hash, and fences every evaluated TypeScript dependency. It records actual evaluated and input hashes and a bundle hash in each report. A CRLF control checks source and patch normalization. The default runner needs neither Git history nor ignored audit notes. + +`source-versions.json` records the audited source graph (276 modules) and the independent main graph at `291b4ddd6f1c1af480169885e0fda7f9c78ff053` (274 modules). Both graphs are accepted explicitly; ten surrounding modules differ because of unrelated audit fixes. The proof therefore does not require those fixes to be stacked. The publication reports were produced through the exported `run({ readSource, output, sourceLabel })` API, reading each non-target source from that named main revision and applying only this product change. The default command also runs directly on that publication tree with the fix and artifact installed. + +Node 26.6.0 and Electron 43.7.0 / Node 24.21.0 both produced the table above against both source graphs. All four executions used the working installation’s external packages. These are source overlays, not historical application or dependency installations. + +At reported v1.4.198 (`e0826956fcfc532f5a1e55b5e081f2e57e553c43`), the resolver, shared recovery controller, and authenticated identity publication match the recorded baseline exactly. The surrounding degraded provider differs, as recorded in `historicalCore`; no whole-v1.4.198 execution or incident attribution is claimed. + +`validation.json` records tests, typecheck, full-file artifact quality, and limits. The four result files contain measured entry counts and exact source/artifact identities. diff --git a/docs/audits/daemon-shared-owner-incarnation-retention/before.config.mjs b/docs/audits/daemon-shared-owner-incarnation-retention/before.config.mjs new file mode 100644 index 00000000000..1b24a9637cb --- /dev/null +++ b/docs/audits/daemon-shared-owner-incarnation-retention/before.config.mjs @@ -0,0 +1,23 @@ +import { resolve } from 'node:path' +import { createRequire } from 'node:module' +import { defineConfig, mergeConfig } from 'vitest/config' +import baseConfig from '../../../config/vitest.config.ts' + +const { loadSources } = createRequire(import.meta.url)('./sources.cjs') +const { before } = loadSources() +const sourcePath = resolve('src/main/daemon/daemon-session-owner-resolution.ts') + +export default mergeConfig( + baseConfig, + defineConfig({ + plugins: [ + { + name: 'shared-owner-incarnation-before-fix', + enforce: 'pre', + transform(_code, id) { + return resolve(id.split('?')[0]) === sourcePath ? { code: before, map: null } : undefined + } + } + ] + }) +) diff --git a/docs/audits/daemon-shared-owner-incarnation-retention/electron-results.json b/docs/audits/daemon-shared-owner-incarnation-retention/electron-results.json new file mode 100644 index 00000000000..5d2ee21f023 --- /dev/null +++ b/docs/audits/daemon-shared-owner-incarnation-retention/electron-results.json @@ -0,0 +1,1582 @@ +{ + "scope": "Actual degraded provider/recovery/resolvers, adapter inventory, identity publication and direct attach; finite inert authenticated transport replies, no native PTY or network", + "runtime": { + "node": "24.21.0", + "acorn": "8.18.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.2", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "148", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "0.0.0", + "simdjson": "4.6.7", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2025c", + "undici": "7.29.1", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "15.0.245.31-electron.0", + "zlib": "1.3.2.1-motley", + "zstd": "1.6.0", + "electron": "43.7.0", + "chrome": "150.0.7871.250" + }, + "sourceLabel": "working-tree", + "crlfReads": 2, + "artifactHashes": { + "sources.cjs": "e7d5bf87dcccdb6dac4ef867da67987a47a8209c88fb8f434bc9f169e8e231cf", + "scenario.cjs": "3a23e25aceb0f1591b3ae9934b1844ea51c8899614a437f9dfa16e26d4812bbf", + "reproduce.cjs": "6f66eb23c9fe171c920fe2fa8fd93127f927034b02e25260ef9d9268dee5d4f6", + "before.config.mjs": "4fcc9a02c35550a0e06e6791aa262fc72198d2aa2a6cf91bef5c59a6c7290f1b", + "source-versions.json": "d858b677fd572d1e7751ef23150985f77cbb6509b1323b721e140b23d98c1cae", + "fix.patch": "7e0554dda37dec5df920c4da9c54472abae949c657ebe938fd4692d0fc54660d" + }, + "phases": { + "before": { + "cycles": 32, + "rows": [ + { + "cycle": 0, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 2 + }, + { + "cycle": 1, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 3 + }, + { + "cycle": 2, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 4 + }, + { + "cycle": 3, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 5 + }, + { + "cycle": 4, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 6 + }, + { + "cycle": 5, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 7 + }, + { + "cycle": 6, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 8 + }, + { + "cycle": 7, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 9 + }, + { + "cycle": 8, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 10 + }, + { + "cycle": 9, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 11 + }, + { + "cycle": 10, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 12 + }, + { + "cycle": 11, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 13 + }, + { + "cycle": 12, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 14 + }, + { + "cycle": 13, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 15 + }, + { + "cycle": 14, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 16 + }, + { + "cycle": 15, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 17 + }, + { + "cycle": 16, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 18 + }, + { + "cycle": 17, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 19 + }, + { + "cycle": 18, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 20 + }, + { + "cycle": 19, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 21 + }, + { + "cycle": 20, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 22 + }, + { + "cycle": 21, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 23 + }, + { + "cycle": 22, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 24 + }, + { + "cycle": 23, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 25 + }, + { + "cycle": 24, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 26 + }, + { + "cycle": 25, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 27 + }, + { + "cycle": 26, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 28 + }, + { + "cycle": 27, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 29 + }, + { + "cycle": 28, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 30 + }, + { + "cycle": 29, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 31 + }, + { + "cycle": 30, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 32 + }, + { + "cycle": 31, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 33 + } + ], + "afterLegacyExit": { + "sharedRoutes": 0, + "attachEntries": 0, + "livenessEntries": 32 + }, + "sameIdSuccessorPreserved": true, + "matchingDirectAttachWithoutInventory": true, + "authoritativeIncarnationMismatchRefused": true, + "unchangedIdentityPreserved": true, + "otherLiveProviderPreserved": true, + "ordinaryExitRetiresBoth": true, + "provenance": { + "evaluatedSources": { + "src/main/daemon/daemon-pty-adapter.ts": "edb242c76bba5e823ebd84e87260bcce564ec928d18ca7d3f10f43c3bcc85c88", + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "f12d5516b6b40859256fa56593ab58484482be39c59b1176773cda58b5ce78f2", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-session-owner-resolution.ts": "4a69ddc724e58ab9eb7607a7ed1f740c5a69b0f809148c03a59f623f275fbe62", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/headless-emulator.ts": "ace96102285c3967cd9544e7696fe73e1ac6df576b476939aa9819f5c298bab6", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "85c68ffd0e20dad097fe32b8fcf39de7c4e89cee75e7c76a3b4699bad3e6913c", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "37a9ff93dd9a227f708597c3eafecf886350e7d3c8b5ad4468149513775a8f4c", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/protocol-version.ts": "397ce01879e98b4c8f6b0068ce8c0d180e34c65663db66f65016790bd31f4e6d", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/shared/terminal-osc-link-retirement.ts": "40afa50eb7f531a2ff8df54b2fb0d86761597b635a1d6009e22503efdbd0113d", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/main/providers/working-directory-validation.ts": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/main/daemon/daemon-pty-runtime-state.ts": "c3424eeeaa1aa8d267df12a6b6b084b6925b45a73cb454f8480d67f8fa564f4e", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390" + }, + "provenanceSources": { + "src/main/daemon/daemon-pty-adapter.ts": "edb242c76bba5e823ebd84e87260bcce564ec928d18ca7d3f10f43c3bcc85c88", + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "f12d5516b6b40859256fa56593ab58484482be39c59b1176773cda58b5ce78f2", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-session-owner-resolution.ts": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/headless-emulator.ts": "ace96102285c3967cd9544e7696fe73e1ac6df576b476939aa9819f5c298bab6", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "85c68ffd0e20dad097fe32b8fcf39de7c4e89cee75e7c76a3b4699bad3e6913c", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "37a9ff93dd9a227f708597c3eafecf886350e7d3c8b5ad4468149513775a8f4c", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/protocol-version.ts": "397ce01879e98b4c8f6b0068ce8c0d180e34c65663db66f65016790bd31f4e6d", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/shared/terminal-osc-link-retirement.ts": "40afa50eb7f531a2ff8df54b2fb0d86761597b635a1d6009e22503efdbd0113d", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/main/providers/working-directory-validation.ts": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/main/daemon/daemon-pty-runtime-state.ts": "c3424eeeaa1aa8d267df12a6b6b084b6925b45a73cb454f8480d67f8fa564f4e", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390" + }, + "bundleSha256": "4eff046149945b64f07bc36d94568ac6166d30f03f5a018396cbf710931407e9" + } + }, + "fixed": { + "cycles": 32, + "rows": [ + { + "cycle": 0, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 1, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 2, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 3, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 4, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 5, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 6, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 7, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 8, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 9, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 10, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 11, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 12, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 13, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 14, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 15, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 16, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 17, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 18, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 19, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 20, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 21, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 22, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 23, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 24, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 25, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 26, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 27, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 28, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 29, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 30, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 31, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + } + ], + "afterLegacyExit": { + "sharedRoutes": 0, + "attachEntries": 0, + "livenessEntries": 0 + }, + "sameIdSuccessorPreserved": true, + "matchingDirectAttachWithoutInventory": true, + "authoritativeIncarnationMismatchRefused": true, + "unchangedIdentityPreserved": true, + "otherLiveProviderPreserved": true, + "ordinaryExitRetiresBoth": true, + "provenance": { + "evaluatedSources": { + "src/main/daemon/daemon-pty-adapter.ts": "edb242c76bba5e823ebd84e87260bcce564ec928d18ca7d3f10f43c3bcc85c88", + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "f12d5516b6b40859256fa56593ab58484482be39c59b1176773cda58b5ce78f2", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-session-owner-resolution.ts": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/headless-emulator.ts": "ace96102285c3967cd9544e7696fe73e1ac6df576b476939aa9819f5c298bab6", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "85c68ffd0e20dad097fe32b8fcf39de7c4e89cee75e7c76a3b4699bad3e6913c", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "37a9ff93dd9a227f708597c3eafecf886350e7d3c8b5ad4468149513775a8f4c", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/protocol-version.ts": "397ce01879e98b4c8f6b0068ce8c0d180e34c65663db66f65016790bd31f4e6d", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/shared/terminal-osc-link-retirement.ts": "40afa50eb7f531a2ff8df54b2fb0d86761597b635a1d6009e22503efdbd0113d", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/providers/working-directory-validation.ts": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/daemon/daemon-pty-runtime-state.ts": "c3424eeeaa1aa8d267df12a6b6b084b6925b45a73cb454f8480d67f8fa564f4e", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390" + }, + "provenanceSources": { + "src/main/daemon/daemon-pty-adapter.ts": "edb242c76bba5e823ebd84e87260bcce564ec928d18ca7d3f10f43c3bcc85c88", + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "f12d5516b6b40859256fa56593ab58484482be39c59b1176773cda58b5ce78f2", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-session-owner-resolution.ts": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/headless-emulator.ts": "ace96102285c3967cd9544e7696fe73e1ac6df576b476939aa9819f5c298bab6", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "85c68ffd0e20dad097fe32b8fcf39de7c4e89cee75e7c76a3b4699bad3e6913c", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "37a9ff93dd9a227f708597c3eafecf886350e7d3c8b5ad4468149513775a8f4c", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/protocol-version.ts": "397ce01879e98b4c8f6b0068ce8c0d180e34c65663db66f65016790bd31f4e6d", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/shared/terminal-osc-link-retirement.ts": "40afa50eb7f531a2ff8df54b2fb0d86761597b635a1d6009e22503efdbd0113d", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/providers/working-directory-validation.ts": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/daemon/daemon-pty-runtime-state.ts": "c3424eeeaa1aa8d267df12a6b6b084b6925b45a73cb454f8480d67f8fa564f4e", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390" + }, + "bundleSha256": "310cba6f434f170b7554db5b9777041865ffc294773ad0c9966f0a0a60ad4885" + } + } + } +} diff --git a/docs/audits/daemon-shared-owner-incarnation-retention/fix.patch b/docs/audits/daemon-shared-owner-incarnation-retention/fix.patch new file mode 100644 index 00000000000..37a7896c132 --- /dev/null +++ b/docs/audits/daemon-shared-owner-incarnation-retention/fix.patch @@ -0,0 +1,11 @@ +diff --git a/src/main/daemon/daemon-session-owner-resolution.ts b/src/main/daemon/daemon-session-owner-resolution.ts +index 1906ebfb35..47fe531ec7 100644 +--- a/src/main/daemon/daemon-session-owner-resolution.ts ++++ b/src/main/daemon/daemon-session-owner-resolution.ts +@@ -54,0 +55,6 @@ export class DaemonSessionOwnerResolver { ++ // Another resolver may already have removed this provider's shared routes. ++ for (const sessionId of this.routeIncarnations.keys()) { ++ if (!this.routes.has(sessionId)) { ++ this.routeIncarnations.delete(sessionId) ++ } ++ } diff --git a/docs/audits/daemon-shared-owner-incarnation-retention/node-results.json b/docs/audits/daemon-shared-owner-incarnation-retention/node-results.json new file mode 100644 index 00000000000..3027ef27dbd --- /dev/null +++ b/docs/audits/daemon-shared-owner-incarnation-retention/node-results.json @@ -0,0 +1,1581 @@ +{ + "scope": "Actual degraded provider/recovery/resolvers, adapter inventory, identity publication and direct attach; finite inert authenticated transport replies, no native PTY or network", + "runtime": { + "node": "26.6.0", + "acorn": "8.17.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.3", + "libffi": "3.7.1", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "147", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "3.6.3", + "simdjson": "4.6.6", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2026a", + "undici": "8.9.0", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "14.6.202.34-node.26", + "zlib": "1.2.12", + "zstd": "1.5.7" + }, + "sourceLabel": "working-tree", + "crlfReads": 2, + "artifactHashes": { + "sources.cjs": "e7d5bf87dcccdb6dac4ef867da67987a47a8209c88fb8f434bc9f169e8e231cf", + "scenario.cjs": "3a23e25aceb0f1591b3ae9934b1844ea51c8899614a437f9dfa16e26d4812bbf", + "reproduce.cjs": "6f66eb23c9fe171c920fe2fa8fd93127f927034b02e25260ef9d9268dee5d4f6", + "before.config.mjs": "4fcc9a02c35550a0e06e6791aa262fc72198d2aa2a6cf91bef5c59a6c7290f1b", + "source-versions.json": "d858b677fd572d1e7751ef23150985f77cbb6509b1323b721e140b23d98c1cae", + "fix.patch": "7e0554dda37dec5df920c4da9c54472abae949c657ebe938fd4692d0fc54660d" + }, + "phases": { + "before": { + "cycles": 32, + "rows": [ + { + "cycle": 0, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 2 + }, + { + "cycle": 1, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 3 + }, + { + "cycle": 2, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 4 + }, + { + "cycle": 3, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 5 + }, + { + "cycle": 4, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 6 + }, + { + "cycle": 5, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 7 + }, + { + "cycle": 6, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 8 + }, + { + "cycle": 7, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 9 + }, + { + "cycle": 8, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 10 + }, + { + "cycle": 9, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 11 + }, + { + "cycle": 10, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 12 + }, + { + "cycle": 11, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 13 + }, + { + "cycle": 12, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 14 + }, + { + "cycle": 13, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 15 + }, + { + "cycle": 14, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 16 + }, + { + "cycle": 15, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 17 + }, + { + "cycle": 16, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 18 + }, + { + "cycle": 17, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 19 + }, + { + "cycle": 18, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 20 + }, + { + "cycle": 19, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 21 + }, + { + "cycle": 20, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 22 + }, + { + "cycle": 21, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 23 + }, + { + "cycle": 22, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 24 + }, + { + "cycle": 23, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 25 + }, + { + "cycle": 24, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 26 + }, + { + "cycle": 25, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 27 + }, + { + "cycle": 26, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 28 + }, + { + "cycle": 27, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 29 + }, + { + "cycle": 28, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 30 + }, + { + "cycle": 29, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 31 + }, + { + "cycle": 30, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 32 + }, + { + "cycle": 31, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 33 + } + ], + "afterLegacyExit": { + "sharedRoutes": 0, + "attachEntries": 0, + "livenessEntries": 32 + }, + "sameIdSuccessorPreserved": true, + "matchingDirectAttachWithoutInventory": true, + "authoritativeIncarnationMismatchRefused": true, + "unchangedIdentityPreserved": true, + "otherLiveProviderPreserved": true, + "ordinaryExitRetiresBoth": true, + "provenance": { + "evaluatedSources": { + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-adapter.ts": "edb242c76bba5e823ebd84e87260bcce564ec928d18ca7d3f10f43c3bcc85c88", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "f12d5516b6b40859256fa56593ab58484482be39c59b1176773cda58b5ce78f2", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/daemon-session-owner-resolution.ts": "4a69ddc724e58ab9eb7607a7ed1f740c5a69b0f809148c03a59f623f275fbe62", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/headless-emulator.ts": "ace96102285c3967cd9544e7696fe73e1ac6df576b476939aa9819f5c298bab6", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "85c68ffd0e20dad097fe32b8fcf39de7c4e89cee75e7c76a3b4699bad3e6913c", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-pty-session-inventory.ts": "37a9ff93dd9a227f708597c3eafecf886350e7d3c8b5ad4468149513775a8f4c", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/protocol-version.ts": "397ce01879e98b4c8f6b0068ce8c0d180e34c65663db66f65016790bd31f4e6d", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/shared/terminal-osc-link-retirement.ts": "40afa50eb7f531a2ff8df54b2fb0d86761597b635a1d6009e22503efdbd0113d", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/working-directory-validation.ts": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/main/daemon/daemon-pty-runtime-state.ts": "c3424eeeaa1aa8d267df12a6b6b084b6925b45a73cb454f8480d67f8fa564f4e", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee" + }, + "provenanceSources": { + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-adapter.ts": "edb242c76bba5e823ebd84e87260bcce564ec928d18ca7d3f10f43c3bcc85c88", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "f12d5516b6b40859256fa56593ab58484482be39c59b1176773cda58b5ce78f2", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/daemon-session-owner-resolution.ts": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/headless-emulator.ts": "ace96102285c3967cd9544e7696fe73e1ac6df576b476939aa9819f5c298bab6", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "85c68ffd0e20dad097fe32b8fcf39de7c4e89cee75e7c76a3b4699bad3e6913c", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-pty-session-inventory.ts": "37a9ff93dd9a227f708597c3eafecf886350e7d3c8b5ad4468149513775a8f4c", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/protocol-version.ts": "397ce01879e98b4c8f6b0068ce8c0d180e34c65663db66f65016790bd31f4e6d", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/shared/terminal-osc-link-retirement.ts": "40afa50eb7f531a2ff8df54b2fb0d86761597b635a1d6009e22503efdbd0113d", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/working-directory-validation.ts": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/main/daemon/daemon-pty-runtime-state.ts": "c3424eeeaa1aa8d267df12a6b6b084b6925b45a73cb454f8480d67f8fa564f4e", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee" + }, + "bundleSha256": "4eff046149945b64f07bc36d94568ac6166d30f03f5a018396cbf710931407e9" + } + }, + "fixed": { + "cycles": 32, + "rows": [ + { + "cycle": 0, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 1, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 2, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 3, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 4, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 5, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 6, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 7, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 8, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 9, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 10, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 11, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 12, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 13, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 14, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 15, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 16, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 17, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 18, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 19, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 20, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 21, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 22, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 23, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 24, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 25, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 26, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 27, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 28, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 29, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 30, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 31, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + } + ], + "afterLegacyExit": { + "sharedRoutes": 0, + "attachEntries": 0, + "livenessEntries": 0 + }, + "sameIdSuccessorPreserved": true, + "matchingDirectAttachWithoutInventory": true, + "authoritativeIncarnationMismatchRefused": true, + "unchangedIdentityPreserved": true, + "otherLiveProviderPreserved": true, + "ordinaryExitRetiresBoth": true, + "provenance": { + "evaluatedSources": { + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-adapter.ts": "edb242c76bba5e823ebd84e87260bcce564ec928d18ca7d3f10f43c3bcc85c88", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "f12d5516b6b40859256fa56593ab58484482be39c59b1176773cda58b5ce78f2", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/daemon-session-owner-resolution.ts": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/headless-emulator.ts": "ace96102285c3967cd9544e7696fe73e1ac6df576b476939aa9819f5c298bab6", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "85c68ffd0e20dad097fe32b8fcf39de7c4e89cee75e7c76a3b4699bad3e6913c", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "37a9ff93dd9a227f708597c3eafecf886350e7d3c8b5ad4468149513775a8f4c", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/protocol-version.ts": "397ce01879e98b4c8f6b0068ce8c0d180e34c65663db66f65016790bd31f4e6d", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/shared/terminal-osc-link-retirement.ts": "40afa50eb7f531a2ff8df54b2fb0d86761597b635a1d6009e22503efdbd0113d", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/main/providers/working-directory-validation.ts": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/main/daemon/daemon-pty-runtime-state.ts": "c3424eeeaa1aa8d267df12a6b6b084b6925b45a73cb454f8480d67f8fa564f4e", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390" + }, + "provenanceSources": { + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-adapter.ts": "edb242c76bba5e823ebd84e87260bcce564ec928d18ca7d3f10f43c3bcc85c88", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "f12d5516b6b40859256fa56593ab58484482be39c59b1176773cda58b5ce78f2", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/daemon-session-owner-resolution.ts": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/headless-emulator.ts": "ace96102285c3967cd9544e7696fe73e1ac6df576b476939aa9819f5c298bab6", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "85c68ffd0e20dad097fe32b8fcf39de7c4e89cee75e7c76a3b4699bad3e6913c", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "37a9ff93dd9a227f708597c3eafecf886350e7d3c8b5ad4468149513775a8f4c", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/protocol-version.ts": "397ce01879e98b4c8f6b0068ce8c0d180e34c65663db66f65016790bd31f4e6d", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/shared/terminal-osc-link-retirement.ts": "40afa50eb7f531a2ff8df54b2fb0d86761597b635a1d6009e22503efdbd0113d", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/main/providers/working-directory-validation.ts": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/main/daemon/daemon-pty-runtime-state.ts": "c3424eeeaa1aa8d267df12a6b6b084b6925b45a73cb454f8480d67f8fa564f4e", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390" + }, + "bundleSha256": "310cba6f434f170b7554db5b9777041865ffc294773ad0c9966f0a0a60ad4885" + } + } + } +} diff --git a/docs/audits/daemon-shared-owner-incarnation-retention/publication-electron-results.json b/docs/audits/daemon-shared-owner-incarnation-retention/publication-electron-results.json new file mode 100644 index 00000000000..9f2e6126e62 --- /dev/null +++ b/docs/audits/daemon-shared-owner-incarnation-retention/publication-electron-results.json @@ -0,0 +1,1574 @@ +{ + "scope": "Actual degraded provider/recovery/resolvers, adapter inventory, identity publication and direct attach; finite inert authenticated transport replies, no native PTY or network", + "runtime": { + "node": "24.21.0", + "acorn": "8.18.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.2", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "148", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "0.0.0", + "simdjson": "4.6.7", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2025c", + "undici": "7.29.1", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "15.0.245.31-electron.0", + "zlib": "1.3.2.1-motley", + "zstd": "1.6.0", + "electron": "43.7.0", + "chrome": "150.0.7871.250" + }, + "sourceLabel": "publication-291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "crlfReads": 2, + "artifactHashes": { + "sources.cjs": "e7d5bf87dcccdb6dac4ef867da67987a47a8209c88fb8f434bc9f169e8e231cf", + "scenario.cjs": "3a23e25aceb0f1591b3ae9934b1844ea51c8899614a437f9dfa16e26d4812bbf", + "reproduce.cjs": "6f66eb23c9fe171c920fe2fa8fd93127f927034b02e25260ef9d9268dee5d4f6", + "before.config.mjs": "4fcc9a02c35550a0e06e6791aa262fc72198d2aa2a6cf91bef5c59a6c7290f1b", + "source-versions.json": "d858b677fd572d1e7751ef23150985f77cbb6509b1323b721e140b23d98c1cae", + "fix.patch": "7e0554dda37dec5df920c4da9c54472abae949c657ebe938fd4692d0fc54660d" + }, + "phases": { + "before": { + "cycles": 32, + "rows": [ + { + "cycle": 0, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 2 + }, + { + "cycle": 1, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 3 + }, + { + "cycle": 2, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 4 + }, + { + "cycle": 3, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 5 + }, + { + "cycle": 4, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 6 + }, + { + "cycle": 5, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 7 + }, + { + "cycle": 6, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 8 + }, + { + "cycle": 7, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 9 + }, + { + "cycle": 8, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 10 + }, + { + "cycle": 9, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 11 + }, + { + "cycle": 10, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 12 + }, + { + "cycle": 11, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 13 + }, + { + "cycle": 12, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 14 + }, + { + "cycle": 13, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 15 + }, + { + "cycle": 14, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 16 + }, + { + "cycle": 15, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 17 + }, + { + "cycle": 16, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 18 + }, + { + "cycle": 17, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 19 + }, + { + "cycle": 18, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 20 + }, + { + "cycle": 19, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 21 + }, + { + "cycle": 20, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 22 + }, + { + "cycle": 21, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 23 + }, + { + "cycle": 22, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 24 + }, + { + "cycle": 23, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 25 + }, + { + "cycle": 24, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 26 + }, + { + "cycle": 25, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 27 + }, + { + "cycle": 26, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 28 + }, + { + "cycle": 27, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 29 + }, + { + "cycle": 28, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 30 + }, + { + "cycle": 29, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 31 + }, + { + "cycle": 30, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 32 + }, + { + "cycle": 31, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 33 + } + ], + "afterLegacyExit": { + "sharedRoutes": 0, + "attachEntries": 0, + "livenessEntries": 32 + }, + "sameIdSuccessorPreserved": true, + "matchingDirectAttachWithoutInventory": true, + "authoritativeIncarnationMismatchRefused": true, + "unchangedIdentityPreserved": true, + "otherLiveProviderPreserved": true, + "ordinaryExitRetiresBoth": true, + "provenance": { + "evaluatedSources": { + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-adapter.ts": "c4868235fea41d1d96b0fb0332b3dc591d4f0a3c98e122e4552c3efbb33791fc", + "src/main/daemon/daemon-pty-listener-emission.ts": "9d1e9b89dfb3bccd2c7e9eb3e0f574fe93772154b57e0ca34ce2779d1b94084f", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "9c76ce16e8d191d542addd0679ed8b730b3eb4c519602b40ffc5705a50c91dce", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/daemon-session-owner-resolution.ts": "4a69ddc724e58ab9eb7607a7ed1f740c5a69b0f809148c03a59f623f275fbe62", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/headless-emulator.ts": "b8bcb28dae2d9fb1b1361c9d32eb69357177943778bf56c2ce9848c82c9ba2a7", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "0a8f8de8430a674e61177563396d0d6322b8d60aa8f182ca8430e5477e4cd16e", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "ef12cd9c10adb536e6884db6e4a00e3915ea2286410228443228fc4972c0ecad", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/protocol-version.ts": "053a152568959f5576050bc83743924772880c85ab6dfb7ce95c6c6acdb02954", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/main/providers/working-directory-validation.ts": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/main/daemon/daemon-pty-runtime-state.ts": "d0898287992c4a3acb14c9c4087c7b28cf11b13417f115d25b445d25d1404f19", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390" + }, + "provenanceSources": { + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-adapter.ts": "c4868235fea41d1d96b0fb0332b3dc591d4f0a3c98e122e4552c3efbb33791fc", + "src/main/daemon/daemon-pty-listener-emission.ts": "9d1e9b89dfb3bccd2c7e9eb3e0f574fe93772154b57e0ca34ce2779d1b94084f", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "9c76ce16e8d191d542addd0679ed8b730b3eb4c519602b40ffc5705a50c91dce", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/daemon-session-owner-resolution.ts": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/headless-emulator.ts": "b8bcb28dae2d9fb1b1361c9d32eb69357177943778bf56c2ce9848c82c9ba2a7", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "0a8f8de8430a674e61177563396d0d6322b8d60aa8f182ca8430e5477e4cd16e", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "ef12cd9c10adb536e6884db6e4a00e3915ea2286410228443228fc4972c0ecad", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/protocol-version.ts": "053a152568959f5576050bc83743924772880c85ab6dfb7ce95c6c6acdb02954", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/main/providers/working-directory-validation.ts": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/main/daemon/daemon-pty-runtime-state.ts": "d0898287992c4a3acb14c9c4087c7b28cf11b13417f115d25b445d25d1404f19", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390" + }, + "bundleSha256": "c55b12ceb272a2c3eab948ba3eb72c2033a7f1a2f66e67c4a372b456611e6d8a" + } + }, + "fixed": { + "cycles": 32, + "rows": [ + { + "cycle": 0, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 1, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 2, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 3, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 4, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 5, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 6, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 7, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 8, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 9, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 10, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 11, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 12, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 13, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 14, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 15, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 16, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 17, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 18, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 19, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 20, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 21, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 22, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 23, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 24, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 25, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 26, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 27, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 28, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 29, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 30, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 31, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + } + ], + "afterLegacyExit": { + "sharedRoutes": 0, + "attachEntries": 0, + "livenessEntries": 0 + }, + "sameIdSuccessorPreserved": true, + "matchingDirectAttachWithoutInventory": true, + "authoritativeIncarnationMismatchRefused": true, + "unchangedIdentityPreserved": true, + "otherLiveProviderPreserved": true, + "ordinaryExitRetiresBoth": true, + "provenance": { + "evaluatedSources": { + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-adapter.ts": "c4868235fea41d1d96b0fb0332b3dc591d4f0a3c98e122e4552c3efbb33791fc", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-listener-emission.ts": "9d1e9b89dfb3bccd2c7e9eb3e0f574fe93772154b57e0ca34ce2779d1b94084f", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "9c76ce16e8d191d542addd0679ed8b730b3eb4c519602b40ffc5705a50c91dce", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/daemon-session-owner-resolution.ts": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/headless-emulator.ts": "b8bcb28dae2d9fb1b1361c9d32eb69357177943778bf56c2ce9848c82c9ba2a7", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "0a8f8de8430a674e61177563396d0d6322b8d60aa8f182ca8430e5477e4cd16e", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "ef12cd9c10adb536e6884db6e4a00e3915ea2286410228443228fc4972c0ecad", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/protocol-version.ts": "053a152568959f5576050bc83743924772880c85ab6dfb7ce95c6c6acdb02954", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/main/providers/working-directory-validation.ts": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/main/daemon/daemon-pty-runtime-state.ts": "d0898287992c4a3acb14c9c4087c7b28cf11b13417f115d25b445d25d1404f19", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee" + }, + "provenanceSources": { + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-adapter.ts": "c4868235fea41d1d96b0fb0332b3dc591d4f0a3c98e122e4552c3efbb33791fc", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-listener-emission.ts": "9d1e9b89dfb3bccd2c7e9eb3e0f574fe93772154b57e0ca34ce2779d1b94084f", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "9c76ce16e8d191d542addd0679ed8b730b3eb4c519602b40ffc5705a50c91dce", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/daemon-session-owner-resolution.ts": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/headless-emulator.ts": "b8bcb28dae2d9fb1b1361c9d32eb69357177943778bf56c2ce9848c82c9ba2a7", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "0a8f8de8430a674e61177563396d0d6322b8d60aa8f182ca8430e5477e4cd16e", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "ef12cd9c10adb536e6884db6e4a00e3915ea2286410228443228fc4972c0ecad", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/protocol-version.ts": "053a152568959f5576050bc83743924772880c85ab6dfb7ce95c6c6acdb02954", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/main/providers/working-directory-validation.ts": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/main/daemon/daemon-pty-runtime-state.ts": "d0898287992c4a3acb14c9c4087c7b28cf11b13417f115d25b445d25d1404f19", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee" + }, + "bundleSha256": "b4999fa3a0b12667092086872d910c358b94730e871b759411c77315525383c2" + } + } + } +} diff --git a/docs/audits/daemon-shared-owner-incarnation-retention/publication-node-results.json b/docs/audits/daemon-shared-owner-incarnation-retention/publication-node-results.json new file mode 100644 index 00000000000..b4fbbf6ed06 --- /dev/null +++ b/docs/audits/daemon-shared-owner-incarnation-retention/publication-node-results.json @@ -0,0 +1,1573 @@ +{ + "scope": "Actual degraded provider/recovery/resolvers, adapter inventory, identity publication and direct attach; finite inert authenticated transport replies, no native PTY or network", + "runtime": { + "node": "26.6.0", + "acorn": "8.17.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.3", + "libffi": "3.7.1", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "147", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "3.6.3", + "simdjson": "4.6.6", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2026a", + "undici": "8.9.0", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "14.6.202.34-node.26", + "zlib": "1.2.12", + "zstd": "1.5.7" + }, + "sourceLabel": "publication-291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "crlfReads": 2, + "artifactHashes": { + "sources.cjs": "e7d5bf87dcccdb6dac4ef867da67987a47a8209c88fb8f434bc9f169e8e231cf", + "scenario.cjs": "3a23e25aceb0f1591b3ae9934b1844ea51c8899614a437f9dfa16e26d4812bbf", + "reproduce.cjs": "6f66eb23c9fe171c920fe2fa8fd93127f927034b02e25260ef9d9268dee5d4f6", + "before.config.mjs": "4fcc9a02c35550a0e06e6791aa262fc72198d2aa2a6cf91bef5c59a6c7290f1b", + "source-versions.json": "d858b677fd572d1e7751ef23150985f77cbb6509b1323b721e140b23d98c1cae", + "fix.patch": "7e0554dda37dec5df920c4da9c54472abae949c657ebe938fd4692d0fc54660d" + }, + "phases": { + "before": { + "cycles": 32, + "rows": [ + { + "cycle": 0, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 2 + }, + { + "cycle": 1, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 3 + }, + { + "cycle": 2, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 4 + }, + { + "cycle": 3, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 5 + }, + { + "cycle": 4, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 6 + }, + { + "cycle": 5, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 7 + }, + { + "cycle": 6, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 8 + }, + { + "cycle": 7, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 9 + }, + { + "cycle": 8, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 10 + }, + { + "cycle": 9, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 11 + }, + { + "cycle": 10, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 12 + }, + { + "cycle": 11, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 13 + }, + { + "cycle": 12, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 14 + }, + { + "cycle": 13, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 15 + }, + { + "cycle": 14, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 16 + }, + { + "cycle": 15, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 17 + }, + { + "cycle": 16, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 18 + }, + { + "cycle": 17, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 19 + }, + { + "cycle": 18, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 20 + }, + { + "cycle": 19, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 21 + }, + { + "cycle": 20, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 22 + }, + { + "cycle": 21, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 23 + }, + { + "cycle": 22, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 24 + }, + { + "cycle": 23, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 25 + }, + { + "cycle": 24, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 26 + }, + { + "cycle": 25, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 27 + }, + { + "cycle": 26, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 28 + }, + { + "cycle": 27, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 29 + }, + { + "cycle": 28, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 30 + }, + { + "cycle": 29, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 31 + }, + { + "cycle": 30, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 32 + }, + { + "cycle": 31, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 33 + } + ], + "afterLegacyExit": { + "sharedRoutes": 0, + "attachEntries": 0, + "livenessEntries": 32 + }, + "sameIdSuccessorPreserved": true, + "matchingDirectAttachWithoutInventory": true, + "authoritativeIncarnationMismatchRefused": true, + "unchangedIdentityPreserved": true, + "otherLiveProviderPreserved": true, + "ordinaryExitRetiresBoth": true, + "provenance": { + "evaluatedSources": { + "src/main/daemon/daemon-pty-adapter.ts": "c4868235fea41d1d96b0fb0332b3dc591d4f0a3c98e122e4552c3efbb33791fc", + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-listener-emission.ts": "9d1e9b89dfb3bccd2c7e9eb3e0f574fe93772154b57e0ca34ce2779d1b94084f", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "9c76ce16e8d191d542addd0679ed8b730b3eb4c519602b40ffc5705a50c91dce", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/daemon-session-owner-resolution.ts": "4a69ddc724e58ab9eb7607a7ed1f740c5a69b0f809148c03a59f623f275fbe62", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/headless-emulator.ts": "b8bcb28dae2d9fb1b1361c9d32eb69357177943778bf56c2ce9848c82c9ba2a7", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "0a8f8de8430a674e61177563396d0d6322b8d60aa8f182ca8430e5477e4cd16e", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "ef12cd9c10adb536e6884db6e4a00e3915ea2286410228443228fc4972c0ecad", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/protocol-version.ts": "053a152568959f5576050bc83743924772880c85ab6dfb7ce95c6c6acdb02954", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/main/providers/working-directory-validation.ts": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/main/daemon/daemon-pty-runtime-state.ts": "d0898287992c4a3acb14c9c4087c7b28cf11b13417f115d25b445d25d1404f19", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390" + }, + "provenanceSources": { + "src/main/daemon/daemon-pty-adapter.ts": "c4868235fea41d1d96b0fb0332b3dc591d4f0a3c98e122e4552c3efbb33791fc", + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-listener-emission.ts": "9d1e9b89dfb3bccd2c7e9eb3e0f574fe93772154b57e0ca34ce2779d1b94084f", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "9c76ce16e8d191d542addd0679ed8b730b3eb4c519602b40ffc5705a50c91dce", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/daemon-session-owner-resolution.ts": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/headless-emulator.ts": "b8bcb28dae2d9fb1b1361c9d32eb69357177943778bf56c2ce9848c82c9ba2a7", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "0a8f8de8430a674e61177563396d0d6322b8d60aa8f182ca8430e5477e4cd16e", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "ef12cd9c10adb536e6884db6e4a00e3915ea2286410228443228fc4972c0ecad", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/protocol-version.ts": "053a152568959f5576050bc83743924772880c85ab6dfb7ce95c6c6acdb02954", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/main/providers/working-directory-validation.ts": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/main/daemon/daemon-pty-runtime-state.ts": "d0898287992c4a3acb14c9c4087c7b28cf11b13417f115d25b445d25d1404f19", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390" + }, + "bundleSha256": "c55b12ceb272a2c3eab948ba3eb72c2033a7f1a2f66e67c4a372b456611e6d8a" + } + }, + "fixed": { + "cycles": 32, + "rows": [ + { + "cycle": 0, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 1, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 2, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 3, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 4, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 5, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 6, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 7, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 8, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 9, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 10, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 11, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 12, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 13, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 14, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 15, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 16, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 17, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 18, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 19, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 20, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 21, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 22, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 23, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 24, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 25, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 26, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 27, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 28, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 29, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 30, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + }, + { + "cycle": 31, + "sharedRoutes": 1, + "attachEntries": 1, + "livenessEntries": 1 + } + ], + "afterLegacyExit": { + "sharedRoutes": 0, + "attachEntries": 0, + "livenessEntries": 0 + }, + "sameIdSuccessorPreserved": true, + "matchingDirectAttachWithoutInventory": true, + "authoritativeIncarnationMismatchRefused": true, + "unchangedIdentityPreserved": true, + "otherLiveProviderPreserved": true, + "ordinaryExitRetiresBoth": true, + "provenance": { + "evaluatedSources": { + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-adapter.ts": "c4868235fea41d1d96b0fb0332b3dc591d4f0a3c98e122e4552c3efbb33791fc", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-listener-emission.ts": "9d1e9b89dfb3bccd2c7e9eb3e0f574fe93772154b57e0ca34ce2779d1b94084f", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "9c76ce16e8d191d542addd0679ed8b730b3eb4c519602b40ffc5705a50c91dce", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/daemon-session-owner-resolution.ts": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/headless-emulator.ts": "b8bcb28dae2d9fb1b1361c9d32eb69357177943778bf56c2ce9848c82c9ba2a7", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "0a8f8de8430a674e61177563396d0d6322b8d60aa8f182ca8430e5477e4cd16e", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "ef12cd9c10adb536e6884db6e4a00e3915ea2286410228443228fc4972c0ecad", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/protocol-version.ts": "053a152568959f5576050bc83743924772880c85ab6dfb7ce95c6c6acdb02954", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/working-directory-validation.ts": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/main/daemon/daemon-pty-runtime-state.ts": "d0898287992c4a3acb14c9c4087c7b28cf11b13417f115d25b445d25d1404f19", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee" + }, + "provenanceSources": { + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-adapter.ts": "c4868235fea41d1d96b0fb0332b3dc591d4f0a3c98e122e4552c3efbb33791fc", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-listener-emission.ts": "9d1e9b89dfb3bccd2c7e9eb3e0f574fe93772154b57e0ca34ce2779d1b94084f", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "9c76ce16e8d191d542addd0679ed8b730b3eb4c519602b40ffc5705a50c91dce", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/daemon-session-owner-resolution.ts": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/headless-emulator.ts": "b8bcb28dae2d9fb1b1361c9d32eb69357177943778bf56c2ce9848c82c9ba2a7", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "0a8f8de8430a674e61177563396d0d6322b8d60aa8f182ca8430e5477e4cd16e", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/daemon-pty-session-inventory.ts": "ef12cd9c10adb536e6884db6e4a00e3915ea2286410228443228fc4972c0ecad", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/protocol-version.ts": "053a152568959f5576050bc83743924772880c85ab6dfb7ce95c6c6acdb02954", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/working-directory-validation.ts": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/main/daemon/daemon-pty-runtime-state.ts": "d0898287992c4a3acb14c9c4087c7b28cf11b13417f115d25b445d25d1404f19", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee" + }, + "bundleSha256": "b4999fa3a0b12667092086872d910c358b94730e871b759411c77315525383c2" + } + } + } +} diff --git a/docs/audits/daemon-shared-owner-incarnation-retention/reproduce.cjs b/docs/audits/daemon-shared-owner-incarnation-retention/reproduce.cjs new file mode 100644 index 00000000000..7113e511f75 --- /dev/null +++ b/docs/audits/daemon-shared-owner-incarnation-retention/reproduce.cjs @@ -0,0 +1,61 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const { load, loadSources, read, sha } = require('./sources.cjs') +const { exercise } = require('./scenario.cjs') + +async function run({ readSource = read, output, sourceLabel = 'working-tree' } = {}) { + assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') + const phases = {} + for (const phase of ['before', 'fixed']) { + const loaded = await load(phase, readSource) + phases[phase] = { ...(await exercise(loaded.api, phase)), provenance: loaded.provenance } + } + let crlfReads = 0 + const crlf = loadSources((file) => { + crlfReads += 1 + return readSource(file).replaceAll('\n', '\r\n') + }) + assert.deepEqual(crlf, loadSources(readSource)) + assert.equal(crlfReads, 2) + const artifacts = [ + 'sources.cjs', + 'scenario.cjs', + 'reproduce.cjs', + 'before.config.mjs', + 'source-versions.json', + 'fix.patch' + ] + const result = { + scope: + 'Actual degraded provider/recovery/resolvers, adapter inventory, identity publication and direct attach; finite inert authenticated transport replies, no native PTY or network', + runtime: process.versions, + sourceLabel, + crlfReads, + artifactHashes: Object.fromEntries( + artifacts.map((file) => [file, sha(read(path.join(__dirname, file)))]) + ), + phases + } + const filename = + output ?? + path.join(__dirname, process.versions.electron ? 'electron-results.json' : 'node-results.json') + fs.writeFileSync(filename, `${JSON.stringify(result, null, 2)}\n`) + console.log( + JSON.stringify({ + output: filename, + before: phases.before.afterLegacyExit, + fixed: phases.fixed.afterLegacyExit, + sourceLabel + }) + ) + return result +} + +module.exports = { run } +if (require.main === module) { + run({ output: process.argv[2] }).catch((error) => { + console.error(error) + process.exitCode = 1 + }) +} diff --git a/docs/audits/daemon-shared-owner-incarnation-retention/scenario.cjs b/docs/audits/daemon-shared-owner-incarnation-retention/scenario.cjs new file mode 100644 index 00000000000..816e5795e15 --- /dev/null +++ b/docs/audits/daemon-shared-owner-incarnation-retention/scenario.cjs @@ -0,0 +1,182 @@ +const assert = require('node:assert/strict') +const path = require('node:path') + +function identity(epoch, pid) { + return { pid, startedAtMs: epoch + 1, launchNonce: `daemon-${pid}-${epoch}` } +} +function makeAdapter(api, name, pid) { + const adapter = new api.DaemonPtyAdapter({ + socketPath: path.join(__dirname, `${name}.sock`), + tokenPath: path.join(__dirname, `${name}.token`) + }) + let sessions = [] + const requests = [] + adapter.client.daemonIdentity = identity(0, pid) + // Only the authenticated transport ports are inert; inventory and identity publication are actual methods. + adapter.client.ensureConnected = async () => {} + adapter.client.ensureConnectedWithin = async () => {} + adapter.client.request = async (type, payload) => { + requests.push(type) + if (type === 'listSessions') { + return { sessions } + } + assert.equal(type, 'createOrAttach') + assert.equal(payload.attachOnly, true) + const found = sessions.find((item) => item.sessionId === payload.sessionId) + assert(found) + return { + isNew: false, + snapshot: null, + pid: found.pid, + incarnationId: found.incarnationId, + shellState: 'unsupported' + } + } + return { + adapter, + requests, + setSessions(value) { + sessions = value + }, + publishIdentity(epoch) { + adapter.client.daemonIdentity = identity(epoch, pid) + return adapter.establishLifecycleLease() + } + } +} +function session(id, incarnationId) { + return { + sessionId: id, + incarnationId, + isAlive: true, + pid: 999999999, + cwd: '/fixture', + cols: 80, + rows: 24 + } +} +async function exercise(api, phase) { + const current = makeAdapter(api, 'current', 999999997) + const legacy = makeAdapter(api, 'legacy', 999999998) + const fallback = { + onData: () => () => {}, + onExit: () => () => {}, + hasPty: () => false, + listProcesses: async () => [] + } + const provider = new api.DegradedDaemonPtyProvider({ + current: current.adapter, + legacy: [legacy.adapter], + fallback + }) + const recovery = provider.ownerRecovery + const attach = recovery.attachResolver + const liveness = recovery.livenessResolver + const rows = [] + try { + await current.publishIdentity(0) + await legacy.publishIdentity(0) + legacy.setSessions([session('legacy-live', 'legacy-incarnation')]) + for (let cycle = 0; cycle < 32; cycle++) { + const id = `current-${cycle}` + current.setSessions([session(id, `incarnation-${cycle}`)]) + // Public discovery populates attach authority; public liveness fills the other resolver. + await provider.discoverDaemonSessions() + assert.equal(await provider.probePtyLiveness(`unmapped-probe-${cycle}`), false) + assert.equal(attach.routeIncarnations.get(id), `incarnation-${cycle}`) + assert.equal(liveness.routeIncarnations.get(id), `incarnation-${cycle}`) + assert.equal(provider.sessionProviders.get(id), current.adapter) + const beforeDuplicate = liveness.routeIncarnations.size + await current.publishIdentity(cycle) + assert.equal(liveness.routeIncarnations.size, beforeDuplicate) + // A new authenticated identity retires the old daemon's routes through actual listeners. + current.setSessions([]) + await current.publishIdentity(cycle + 1) + assert.equal(provider.sessionProviders.has(id), false) + assert.equal(attach.routeIncarnations.has(id), false) + assert.equal(liveness.routeIncarnations.has(id), phase === 'before') + assert.equal(provider.sessionProviders.get('legacy-live'), legacy.adapter) + assert.equal(attach.routeIncarnations.get('legacy-live'), 'legacy-incarnation') + assert.equal(liveness.routeIncarnations.get('legacy-live'), 'legacy-incarnation') + rows.push({ + cycle, + sharedRoutes: provider.sessionProviders.size, + attachEntries: attach.routeIncarnations.size, + livenessEntries: liveness.routeIncarnations.size + }) + } + assert.equal(provider.sessionProviders.size, 1) + assert.equal(attach.routeIncarnations.size, 1) + assert.equal(liveness.routeIncarnations.size, phase === 'before' ? 33 : 1) + legacy.adapter.client.eventListeners.each((listener) => + listener({ + type: 'event', + event: 'exit', + sessionId: 'legacy-live', + payload: { code: 0, incarnationId: 'legacy-incarnation' } + }) + ) + assert.equal(provider.sessionProviders.size, 0) + assert.equal(attach.routeIncarnations.size, 0) + assert.equal(liveness.routeIncarnations.size, phase === 'before' ? 32 : 0) + const afterLegacyExit = { + sharedRoutes: provider.sessionProviders.size, + attachEntries: attach.routeIncarnations.size, + livenessEntries: liveness.routeIncarnations.size + } + legacy.setSessions([]) + current.setSessions([session('same-id', 'old-incarnation')]) + await provider.discoverDaemonSessions() + await provider.probePtyLiveness('unmapped-old') + current.setSessions([]) + legacy.setSessions([session('same-id', 'new-incarnation')]) + await provider.discoverDaemonSessions() + await provider.probePtyLiveness('unmapped-new') + await current.publishIdentity(33) + assert.equal(provider.sessionProviders.get('same-id'), legacy.adapter) + assert.equal(attach.routeIncarnations.get('same-id'), 'new-incarnation') + assert.equal(liveness.routeIncarnations.get('same-id'), 'new-incarnation') + current.requests.length = 0 + legacy.requests.length = 0 + const attached = await provider.spawn({ + sessionId: 'same-id', + attachOnly: true, + cols: 80, + rows: 24, + expectedIncarnationId: 'new-incarnation', + expectedIncarnationIsAuthoritative: true + }) + assert.equal(attached.id, 'same-id') + assert.equal(attached.incarnationId, 'new-incarnation') + assert.equal(attached.isReattach, true) + assert.deepEqual(current.requests, []) + assert.deepEqual(legacy.requests, ['createOrAttach']) + await assert.rejects( + provider.spawn({ + sessionId: 'same-id', + attachOnly: true, + cols: 80, + rows: 24, + expectedIncarnationId: 'retired-incarnation', + expectedIncarnationIsAuthoritative: true + }), + { name: 'TerminalSessionOwnerUnverifiedError' } + ) + assert.equal(legacy.requests.filter((type) => type === 'createOrAttach').length, 1) + return { + cycles: 32, + rows, + afterLegacyExit, + sameIdSuccessorPreserved: true, + matchingDirectAttachWithoutInventory: true, + authoritativeIncarnationMismatchRefused: true, + unchangedIdentityPreserved: true, + otherLiveProviderPreserved: true, + ordinaryExitRetiresBoth: true + } + } finally { + provider.dispose() + } +} + +module.exports = { exercise } diff --git a/docs/audits/daemon-shared-owner-incarnation-retention/source-versions.json b/docs/audits/daemon-shared-owner-incarnation-retention/source-versions.json new file mode 100644 index 00000000000..b8022423ee8 --- /dev/null +++ b/docs/audits/daemon-shared-owner-incarnation-retention/source-versions.json @@ -0,0 +1,1407 @@ +{ + "sourcePath": "src/main/daemon/daemon-session-owner-resolution.ts", + "baselineSha256": "4a69ddc724e58ab9eb7607a7ed1f740c5a69b0f809148c03a59f623f275fbe62", + "fixedSha256": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb", + "dependencies": { + "src/main/daemon/degraded-daemon-pty-provider.ts": [ + "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd" + ], + "src/main/daemon/daemon-pty-adapter.ts": [ + "edb242c76bba5e823ebd84e87260bcce564ec928d18ca7d3f10f43c3bcc85c88", + "c4868235fea41d1d96b0fb0332b3dc591d4f0a3c98e122e4552c3efbb33791fc" + ], + "src/main/daemon/types.ts": [ + "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d" + ], + "src/main/daemon/daemon-pty-daemon-recovery.ts": [ + "f12d5516b6b40859256fa56593ab58484482be39c59b1176773cda58b5ce78f2", + "9c76ce16e8d191d542addd0679ed8b730b3eb4c519602b40ffc5705a50c91dce" + ], + "src/main/daemon/degraded-daemon-owner-recovery.ts": [ + "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6" + ], + "src/main/providers/pty-process-inspection.ts": [ + "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20" + ], + "src/main/daemon/degraded-daemon-session-routing.ts": [ + "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b" + ], + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": [ + "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb" + ], + "src/main/daemon/combine-unsubscribes.ts": [ + "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4" + ], + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": [ + "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff" + ], + "src/main/daemon/daemon-errors.ts": [ + "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac" + ], + "src/main/daemon/daemon-protocol-version.ts": [ + "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd" + ], + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": [ + "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0" + ], + "src/main/daemon/daemon-health.ts": [ + "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114" + ], + "src/main/daemon/daemon-tcc-attribution.ts": [ + "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923" + ], + "src/main/daemon/daemon-bundle-staleness.ts": [ + "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1" + ], + "src/main/daemon/daemon-endpoint-errors.ts": [ + "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7" + ], + "src/shared/terminal-process-inspection.ts": [ + "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b" + ], + "src/main/daemon/daemon-endpoint-ownership.ts": [ + "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc" + ], + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": [ + "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1" + ], + "src/main/daemon/daemon-durable-history-snapshot.ts": [ + "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072" + ], + "src/main/daemon/daemon-pid-identity.ts": [ + "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190" + ], + "src/main/daemon/daemon-spawner.ts": [ + "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017" + ], + "src/main/daemon/daemon-pid-file-parse.ts": [ + "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51" + ], + "src/main/daemon/ndjson.ts": [ + "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91" + ], + "src/shared/main-process-ndjson-framer.ts": [ + "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490" + ], + "src/main/daemon/daemon-process-start-time.ts": [ + "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3" + ], + "src/main/daemon/daemon-process-identity-query.ts": [ + "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b" + ], + "src/main/daemon/daemon-respawn-throttle.ts": [ + "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645" + ], + "src/main/daemon/daemon-endpoint-probe.ts": [ + "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52" + ], + "src/main/daemon/daemon-pty-connection-lifecycle.ts": [ + "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef" + ], + "src/main/daemon/daemon-request-deadline.ts": [ + "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10" + ], + "src/main/daemon/terminal-history-dimensions.ts": [ + "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b" + ], + "src/main/daemon/headless-emulator.ts": [ + "ace96102285c3967cd9544e7696fe73e1ac6df576b476939aa9819f5c298bab6", + "b8bcb28dae2d9fb1b1361c9d32eb69357177943778bf56c2ce9848c82c9ba2a7" + ], + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": [ + "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c" + ], + "src/main/daemon/cold-restore-replay-writer.ts": [ + "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5" + ], + "src/main/daemon/daemon-restore-scrollback-depth.ts": [ + "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d" + ], + "src/shared/process-output-field-scanner.ts": [ + "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5" + ], + "src/main/startup/startup-diagnostics.ts": [ + "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e" + ], + "src/main/daemon/daemon-pty-event-subscriptions.ts": [ + "85c68ffd0e20dad097fe32b8fcf39de7c4e89cee75e7c76a3b4699bad3e6913c", + "0a8f8de8430a674e61177563396d0d6322b8d60aa8f182ca8430e5477e4cd16e" + ], + "src/main/daemon/daemon-listener-registry.ts": [ + "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449" + ], + "src/main/daemon/daemon-endpoint-incarnation.ts": [ + "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07" + ], + "src/main/daemon/daemon-audit-classifier.ts": [ + "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b" + ], + "src/shared/terminal-scrollback-policy.ts": [ + "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17" + ], + "src/main/daemon/headless-emulator-modes.ts": [ + "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807" + ], + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": [ + "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca" + ], + "src/main/daemon/terminal-mouse-mode-mirror.ts": [ + "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1" + ], + "src/shared/terminal-serialize-absolute-cursor.ts": [ + "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9" + ], + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": [ + "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d" + ], + "src/shared/terminal-partial-escape-tail.ts": [ + "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29" + ], + "src/main/daemon/terminal-frame-restore-sequences.ts": [ + "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7" + ], + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": [ + "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee" + ], + "src/main/daemon/terminal-view-attribute-responder.ts": [ + "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7" + ], + "src/main/daemon/startup-device-attributes-responder.ts": [ + "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b" + ], + "src/shared/terminal-cursor-line-context.ts": [ + "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1" + ], + "src/shared/terminal-osc-link-retirement.ts": [ + "40afa50eb7f531a2ff8df54b2fb0d86761597b635a1d6009e22503efdbd0113d" + ], + "src/shared/terminal-unicode-provider.ts": [ + "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f" + ], + "src/main/daemon/headless-osc-link-ranges.ts": [ + "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca" + ], + "src/main/daemon/xterm-env-polyfill.ts": [ + "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9" + ], + "src/main/daemon/daemon-pty-session-inventory.ts": [ + "37a9ff93dd9a227f708597c3eafecf886350e7d3c8b5ad4468149513775a8f4c", + "ef12cd9c10adb536e6884db6e4a00e3915ea2286410228443228fc4972c0ecad" + ], + "src/main/daemon/daemon-incarnation-evidence.ts": [ + "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb" + ], + "src/shared/terminal-mode-reset-profiles.ts": [ + "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049" + ], + "src/shared/own-retained-string.ts": [ + "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2" + ], + "src/main/daemon/osc7-uri-extraction.ts": [ + "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7" + ], + "src/shared/agent-detection.ts": [ + "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651" + ], + "src/main/daemon/osc7-file-uri.ts": [ + "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a" + ], + "src/shared/terminal-escape-introducer.ts": [ + "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5" + ], + "src/shared/terminal-view-attributes.ts": [ + "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305" + ], + "src/main/providers/pty-process-list-admission.ts": [ + "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356" + ], + "src/shared/wsl-paths.ts": ["1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a"], + "src/shared/claimed-agent-pty-owner-snapshot.ts": [ + "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5" + ], + "src/shared/agent-session-host-authority.ts": [ + "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7" + ], + "src/main/daemon/daemon-pty-process-inspection.ts": [ + "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8" + ], + "src/main/daemon/pty-session-id.ts": [ + "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878" + ], + "src/shared/agent-title-core.ts": [ + "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d" + ], + "src/shared/opencode-terminal-title.ts": [ + "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec" + ], + "src/shared/agent-title-identity.ts": [ + "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453" + ], + "src/shared/agent-title-status.ts": [ + "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb" + ], + "src/shared/claimed-agent-pty-owner.ts": [ + "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1" + ], + "src/shared/agent-name-token-match.ts": [ + "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8" + ], + "src/shared/osc-title-extraction.ts": [ + "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a" + ], + "src/shared/shell-process-detection.ts": [ + "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f" + ], + "src/shared/owned-utf16-suffix.ts": [ + "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + ], + "src/main/daemon/daemon-process-inspection.ts": [ + "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683" + ], + "src/main/agent-hooks/managed-hook-owner-identity.ts": [ + "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c" + ], + "src/main/daemon/daemon-incarnation-evidence-types.ts": [ + "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8" + ], + "src/shared/foreground-process-evidence.ts": [ + "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c" + ], + "src/shared/pty-incarnation.ts": [ + "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5" + ], + "src/shared/terminal-tab-id.ts": [ + "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82" + ], + "src/shared/stable-pane-id.ts": [ + "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884" + ], + "src/shared/protocol-version.ts": [ + "397ce01879e98b4c8f6b0068ce8c0d180e34c65663db66f65016790bd31f4e6d", + "053a152568959f5576050bc83743924772880c85ab6dfb7ce95c6c6acdb02954" + ], + "src/shared/agent-session-resume.ts": [ + "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43" + ], + "src/shared/terminal-title-classification-memo.ts": [ + "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda" + ], + "src/shared/pi-state-title-marker.ts": [ + "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8" + ], + "src/shared/pi-compatible-synthetic-title.ts": [ + "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f" + ], + "src/shared/pty-session-id-format.ts": [ + "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b" + ], + "src/main/daemon/daemon-pty-buffer-snapshots.ts": [ + "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9" + ], + "src/shared/terminal-title-wrapper-segments.ts": [ + "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78" + ], + "src/shared/agent-title-decoration.ts": [ + "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41" + ], + "src/shared/terminal-title-agent-type.ts": [ + "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864" + ], + "src/shared/terminal-surface-id.ts": [ + "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da" + ], + "src/shared/skill-install-capability.ts": [ + "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19" + ], + "src/shared/remote-server-update.ts": [ + "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12" + ], + "src/shared/workspace-scope.ts": [ + "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581" + ], + "src/main/daemon/daemon-session-scrollback-window.ts": [ + "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68" + ], + "src/shared/terminal-kitty-keyboard-flags.ts": [ + "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225" + ], + "src/main/daemon/daemon-pty-session-control.ts": [ + "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8" + ], + "src/shared/pane-agent-identity-adapter.ts": [ + "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9" + ], + "src/main/daemon/daemon-pty-applied-size.ts": [ + "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0" + ], + "src/main/providers/pty-default-cwd.ts": [ + "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f" + ], + "src/main/daemon/wsl-cold-restore-cwd.ts": [ + "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200" + ], + "src/main/daemon/daemon-pty-session-input.ts": [ + "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037" + ], + "src/main/daemon/daemon-pty-lifecycle-errors.ts": [ + "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1" + ], + "src/shared/agent-title-evidence.ts": [ + "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea" + ], + "src/shared/pane-agent-evidence-sources.ts": [ + "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f" + ], + "src/main/providers/pty-path-safety.ts": [ + "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0" + ], + "src/main/daemon/daemon-pty-size.ts": [ + "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741" + ], + "src/shared/pty-write-settlement.ts": [ + "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6" + ], + "src/main/daemon/daemon-pty-session-spawn.ts": [ + "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114" + ], + "src/main/providers/pty-write-unavailable-error.ts": [ + "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40" + ], + "src/shared/tui-agent-display-names.ts": [ + "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296" + ], + "src/shared/synthetic-agent-title.ts": [ + "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922" + ], + "src/shared/agent-process-recognition.ts": [ + "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4" + ], + "src/main/daemon/session-shell-ready-barrier.ts": [ + "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109" + ], + "src/main/daemon/daemon-pty-spawn-result.ts": [ + "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e" + ], + "src/shared/codex-startup-delivery.ts": [ + "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad" + ], + "src/main/daemon/daemon-adoption-telemetry-event.ts": [ + "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d" + ], + "src/main/wsl-env.ts": ["66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52"], + "src/main/daemon/terminal-history-seed-segments.ts": [ + "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5" + ], + "src/main/terminal-history.ts": [ + "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013" + ], + "src/main/daemon/shell-ready.ts": [ + "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669" + ], + "src/main/providers/local-pty-utils.ts": [ + "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25" + ], + "src/shared/shell-ready-marker-timing.ts": [ + "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2" + ], + "src/main/daemon/wsl-session-context.ts": [ + "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a" + ], + "src/main/shell-prompt-readiness-probe.ts": [ + "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73" + ], + "src/main/daemon/post-ready-flush-gate.ts": [ + "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac" + ], + "src/main/shell-startup-output-scanner.ts": [ + "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7" + ], + "src/shared/command-token-scanner.ts": [ + "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0" + ], + "src/shared/tui-agent-config.ts": [ + "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad" + ], + "src/shared/agent-node-entrypoint-identities.ts": [ + "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4" + ], + "src/shared/agent-headless-command.ts": [ + "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87" + ], + "src/main/daemon/daemon-history-recovery-freeze.ts": [ + "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0" + ], + "src/shared/wsl-env.ts": ["9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88"], + "src/main/daemon/daemon-attach-only-retirement.ts": [ + "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca" + ], + "src/main/daemon/daemon-pty-spawn-request.ts": [ + "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e" + ], + "src/main/daemon/daemon-pty-provider-sequence.ts": [ + "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb" + ], + "src/main/telemetry/client.ts": [ + "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0" + ], + "src/shared/app-environment.ts": [ + "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140" + ], + "src/shared/daemon-adoption-telemetry.ts": [ + "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5" + ], + "src/shared/daemon-lifecycle-telemetry.ts": [ + "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc" + ], + "src/main/line-editor-ready-output-scanner.ts": [ + "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293" + ], + "src/shared/pty-slave-line-discipline-echo.ts": [ + "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4" + ], + "src/shared/shell-process-readiness.ts": [ + "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd" + ], + "src/main/telemetry/validator.ts": [ + "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99" + ], + "src/main/telemetry/cohort-classifier.ts": [ + "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f" + ], + "src/main/telemetry/consent.ts": [ + "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39" + ], + "src/main/telemetry/burst-cap.ts": [ + "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c" + ], + "src/main/providers/working-directory-validation.ts": [ + "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212", + "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043" + ], + "src/shared/node-pty-spawn-helper.ts": [ + "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d" + ], + "src/main/providers/macos-tcc-login-shell.ts": [ + "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba" + ], + "src/main/daemon/terminal-history-seed-chunks.ts": [ + "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f" + ], + "src/main/daemon/daemon-pty-runtime-state.ts": [ + "c3424eeeaa1aa8d267df12a6b6b084b6925b45a73cb454f8480d67f8fa564f4e", + "d0898287992c4a3acb14c9c4087c7b28cf11b13417f115d25b445d25d1404f19" + ], + "src/shared/print-mode-headless-command.ts": [ + "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a" + ], + "src/shared/prime-agent-headless-command.ts": [ + "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3" + ], + "src/shared/ante-headless-command.ts": [ + "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620" + ], + "src/main/shell-startup-identity-scanner.ts": [ + "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6" + ], + "src/main/shell-ready-marker-scanner.ts": [ + "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d" + ], + "src/shared/orca-cli-command-name.ts": [ + "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0" + ], + "src/main/wsl.ts": ["d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a"], + "src/shared/worktree/id.ts": [ + "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30" + ], + "src/shared/process-table-snapshot.ts": [ + "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9" + ], + "src/shared/local-windows-terminal-runtime.ts": [ + "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c" + ], + "src/main/terminal-history-id.ts": [ + "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0" + ], + "src/main/fish-history-session.ts": [ + "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb" + ], + "src/main/daemon/daemon-shell-ready-marker.ts": [ + "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21" + ], + "src/main/shell-wrapper-content-address.ts": [ + "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028" + ], + "src/main/shell-wrapper-file-writer.ts": [ + "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232" + ], + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": [ + "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b" + ], + "src/main/worktree-history-file-path.ts": [ + "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa" + ], + "src/shared/telemetry-events.ts": [ + "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6" + ], + "src/main/pty/codex-shell-launch-preflight.ts": [ + "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca" + ], + "src/main/terminal-history-paths.ts": [ + "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75" + ], + "src/main/zsh-wrapper-dir-ownership.ts": [ + "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e" + ], + "src/main/shell-templates.ts": [ + "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a" + ], + "src/main/powershell-osc133-bootstrap.ts": [ + "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446" + ], + "src/main/shell-startup-features.ts": [ + "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7" + ], + "src/shared/priority-semaphore.ts": [ + "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5" + ], + "src/main/providers/macos-login-session-pty-probe.ts": [ + "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63" + ], + "src/shared/child-process/run-process.ts": [ + "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad" + ], + "src/shared/cross-platform-path.ts": [ + "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3" + ], + "src/main/daemon/history-reader.ts": [ + "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d" + ], + "src/main/daemon/client.ts": [ + "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14" + ], + "src/main/daemon/daemon-audit-eligibility-event.ts": [ + "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137" + ], + "src/main/daemon/daemon-checkpoint-session-queue.ts": [ + "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b" + ], + "src/main/daemon/cold-restore-payload-cache.ts": [ + "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77" + ], + "src/main/daemon/history-manager.ts": [ + "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c" + ], + "src/main/wsl-directory-probe-command.ts": [ + "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246" + ], + "src/main/wsl-distro-list-output.ts": [ + "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc" + ], + "src/main/wsl-availability.ts": [ + "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf" + ], + "src/main/wsl-interop-spawn-directory.ts": [ + "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf" + ], + "src/main/wsl-running-distro-cache.ts": [ + "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a" + ], + "src/main/wsl-distro-retry.ts": [ + "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be" + ], + "src/main/cli/bundled-cli-launcher-path.ts": [ + "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4" + ], + "src/shared/powershell-command-encoding.ts": [ + "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9" + ], + "src/main/pty/omp-shell-wrapper.ts": [ + "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf" + ], + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": [ + "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b" + ], + "src/main/zsh-startup-wrapper-builder.ts": [ + "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26" + ], + "src/main/bash-prompt-command-composition.ts": [ + "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d" + ], + "src/main/pty/posix-shell-startup-command.ts": [ + "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c" + ], + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": [ + "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be" + ], + "src/shared/telemetry-app-event-schemas.ts": [ + "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831" + ], + "src/shared/telemetry-daemon-event-schemas.ts": [ + "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac" + ], + "src/shared/telemetry-property-schemas.ts": [ + "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd" + ], + "src/shared/telemetry-event-classification.ts": [ + "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e" + ], + "src/shared/telemetry-event-registry.ts": [ + "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9" + ], + "src/shared/wsl-login-shell-command.ts": [ + "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075" + ], + "src/shared/child-process/process-spec.ts": [ + "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289" + ], + "src/shared/child-process/bounded-output-sink.ts": [ + "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865" + ], + "src/shared/child-process/child-termination-reporter.ts": [ + "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0" + ], + "src/shared/child-process/process-tree-termination.ts": [ + "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120" + ], + "src/shared/child-process/spawn-resolution.ts": [ + "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc" + ], + "src/main/daemon/history-paths.ts": [ + "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63" + ], + "src/main/daemon/terminal-history-log.ts": [ + "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb" + ], + "src/main/daemon/terminal-history-file-limits.ts": [ + "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a" + ], + "src/main/daemon/terminal-history-recovery-quarantine.ts": [ + "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85" + ], + "src/main/daemon/terminal-history-file-reader.ts": [ + "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae" + ], + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": [ + "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441" + ], + "src/main/daemon/terminal-history-restorable-retention.ts": [ + "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d" + ], + "src/main/daemon/daemon-private-file-modes.ts": [ + "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615" + ], + "src/main/daemon/terminal-history-session-tombstone.ts": [ + "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6" + ], + "src/main/daemon/terminal-history-metadata.ts": [ + "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084" + ], + "src/main/daemon/terminal-history-session-files.ts": [ + "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb" + ], + "src/main/daemon/terminal-history-cold-restore-info.ts": [ + "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680" + ], + "src/main/daemon/terminal-history-checkpoint-reader.ts": [ + "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1" + ], + "src/main/daemon/terminal-history-recovery-freezes.ts": [ + "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a" + ], + "src/main/daemon/terminal-history-session-writer.ts": [ + "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856" + ], + "src/main/daemon/terminal-history-mutation-tracker.ts": [ + "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e" + ], + "src/shared/star-nag-telemetry.ts": [ + "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b" + ], + "src/shared/gh-star-source.ts": [ + "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe" + ], + "src/shared/feature-interactions.ts": [ + "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a" + ], + "src/shared/daemon-audit-eligibility.ts": [ + "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446" + ], + "src/shared/agent-hook-types.ts": [ + "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b" + ], + "src/shared/telemetry-feature-education-event-schemas.ts": [ + "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe" + ], + "src/shared/telemetry-native-feature-event-schemas.ts": [ + "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d" + ], + "src/main/daemon/daemon-client-listener-registry.ts": [ + "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b" + ], + "src/main/daemon/daemon-client-pending-requests.ts": [ + "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b" + ], + "src/main/daemon/daemon-client-hello-handshake.ts": [ + "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73" + ], + "src/main/daemon/daemon-client-ndjson-readers.ts": [ + "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee" + ], + "src/shared/telemetry-onboarding-event-schemas.ts": [ + "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756" + ], + "src/main/daemon/daemon-client-notify-settlement.ts": [ + "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25" + ], + "src/main/daemon/daemon-client-socket-connect.ts": [ + "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76" + ], + "src/shared/telemetry-repository-event-schemas.ts": [ + "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572" + ], + "src/main/daemon/daemon-client-rpc-request.ts": [ + "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b" + ], + "src/shared/child-process/windows-cmd-shim-resolution.ts": [ + "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3" + ], + "src/shared/child-process/windows-command-line.ts": [ + "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca" + ], + "src/shared/workspace-source.ts": [ + "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29" + ], + "src/shared/feature-wall-tour-depth.ts": [ + "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb" + ], + "src/shared/setup-script-import-providers.ts": [ + "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a" + ], + "src/shared/child-process/process-tree-kill-gate.ts": [ + "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab" + ], + "src/shared/node-bounded-file-reader.ts": [ + "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb" + ], + "src/main/daemon/terminal-checkpoint-serializer.ts": [ + "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375" + ], + "src/shared/terminal-osc-link-ranges.ts": [ + "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390" + ], + "src/shared/terminal-owner.ts": [ + "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309" + ], + "src/main/host-tree-removal.ts": [ + "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97" + ], + "src/shared/feature-interaction-categories.ts": [ + "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d" + ], + "src/main/daemon/node-pty-error-hints.ts": [ + "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a" + ], + "src/shared/feature-interaction-catalog.ts": [ + "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80" + ], + "src/shared/feature-interaction-usage-buckets.ts": [ + "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff" + ], + "src/shared/feature-education-telemetry.ts": [ + "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00" + ], + "src/shared/feature-wall-setup-steps.ts": [ + "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d" + ], + "src/shared/feature-wall-telemetry.ts": [ + "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693" + ], + "src/main/asar-transparent-fs.ts": [ + "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee" + ], + "src/shared/telemetry-onboarding-foundation-schemas.ts": [ + "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3" + ], + "src/shared/windows-transient-lock-removal.ts": [ + "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2" + ], + "src/shared/nested-repo-telemetry.ts": [ + "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7" + ], + "src/main/daemon/daemon-pty-listener-emission.ts": [ + "9d1e9b89dfb3bccd2c7e9eb3e0f574fe93772154b57e0ca34ce2779d1b94084f" + ] + }, + "workingRevision": "6ab6802df90553d2a26e9a4c7a519ae11d4b5e09", + "publicationRevision": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "reportedRevision": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "historicalCore": { + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "src/main/daemon/daemon-session-owner-resolution.ts": "4a69ddc724e58ab9eb7607a7ed1f740c5a69b0f809148c03a59f623f275fbe62", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef" + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "src/main/daemon/daemon-session-owner-resolution.ts": "4a69ddc724e58ab9eb7607a7ed1f740c5a69b0f809148c03a59f623f275fbe62", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-pty-provider.ts": "108dc9bf12eaaaa1f446c1a86e264611c5a28cd9dfc4f2e8bc59baca168a9d59", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef" + } + }, + "workingEvaluated": { + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-adapter.ts": "edb242c76bba5e823ebd84e87260bcce564ec928d18ca7d3f10f43c3bcc85c88", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "f12d5516b6b40859256fa56593ab58484482be39c59b1176773cda58b5ce78f2", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-session-owner-resolution.ts": "4a69ddc724e58ab9eb7607a7ed1f740c5a69b0f809148c03a59f623f275fbe62", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/headless-emulator.ts": "ace96102285c3967cd9544e7696fe73e1ac6df576b476939aa9819f5c298bab6", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "85c68ffd0e20dad097fe32b8fcf39de7c4e89cee75e7c76a3b4699bad3e6913c", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/shared/terminal-osc-link-retirement.ts": "40afa50eb7f531a2ff8df54b2fb0d86761597b635a1d6009e22503efdbd0113d", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/main/daemon/daemon-pty-session-inventory.ts": "37a9ff93dd9a227f708597c3eafecf886350e7d3c8b5ad4468149513775a8f4c", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/protocol-version.ts": "397ce01879e98b4c8f6b0068ce8c0d180e34c65663db66f65016790bd31f4e6d", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/providers/working-directory-validation.ts": "0ef75b5d5242ad7e5423bde2cb7fe633a8f437fc545f22ca04612782fc2de212", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/main/daemon/daemon-pty-runtime-state.ts": "c3424eeeaa1aa8d267df12a6b6b084b6925b45a73cb454f8480d67f8fa564f4e", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7" + }, + "publicationEvaluated": { + "src/main/daemon/degraded-daemon-pty-provider.ts": "a2b95e457bd7819bfe1a56f5dde2fd6785e5a731801d7f75d2f6ed657c9b50cd", + "src/main/daemon/daemon-pty-adapter.ts": "c4868235fea41d1d96b0fb0332b3dc591d4f0a3c98e122e4552c3efbb33791fc", + "src/main/daemon/degraded-daemon-owner-recovery.ts": "74f7b2000d3a637e4b509e29cceebeccb36a791a8fba34bb6e0811cedb7311c6", + "src/main/daemon/degraded-daemon-fallback-shutdown.ts": "12d2cf65777e5ac8d016536503da52f48eaed2f2a297600f5a9746b85f7e6efb", + "src/main/providers/pty-process-inspection.ts": "aad924d84e3ba26c8d5a48102518a9ee0edfa3e47afe0b63537c1a3d92b35b20", + "src/main/daemon/combine-unsubscribes.ts": "4d468d5bdb8b30fceb4044b276f2f50a1cd4e73845562eedf2f0effd4a8532a4", + "src/main/daemon/degraded-daemon-session-routing.ts": "d0cf9bb317fb0e8b1621861db1241c00b3d5e5bcd4574ac6d2c9875960087e6b", + "src/main/daemon/degraded-daemon-fresh-spawn-routing.ts": "c917dfdf1e4cf25b5b3d6249e8307390c4fb2412ebe999d66942403d49ea76ff", + "src/shared/terminal-process-inspection.ts": "790ac705cf1d12bf805c868d558b75073173c59dd1399596f1e7d79e0bcf534b", + "src/main/daemon/daemon-session-owner-resolution.ts": "4a69ddc724e58ab9eb7607a7ed1f740c5a69b0f809148c03a59f623f275fbe62", + "src/main/daemon/types.ts": "ac253a9fe626ab585d7b988df22c1c79b3dd63510c563538ca50876c6dd6f96d", + "src/main/daemon/daemon-pty-listener-emission.ts": "9d1e9b89dfb3bccd2c7e9eb3e0f574fe93772154b57e0ca34ce2779d1b94084f", + "src/main/daemon/daemon-pty-daemon-recovery.ts": "9c76ce16e8d191d542addd0679ed8b730b3eb4c519602b40ffc5705a50c91dce", + "src/main/daemon/daemon-errors.ts": "d6775c72188f400a92e1658e70df76f8b32437ed94d82eee484a404d69a3ffac", + "src/main/daemon/daemon-protocol-version.ts": "9f26d58414fd35891ebc2912cf8e96488b72248dc27f8736dff5d53fd8b27fcd", + "src/main/daemon/daemon-pty-checkpoint-persistence.ts": "2cbe518533013d4208e10c34c40ece45368c11366a9db898fc043c9d19b7fad0", + "src/main/daemon/daemon-health.ts": "c1ba2c5c687bfbf6d99ee82f51b81d9eac238ff90f48af23a086d10ceb2ed114", + "src/main/daemon/daemon-bundle-staleness.ts": "1742a353bedb6cd0685f213feaa5652fbf5ac65d228c52a93426a19c239783c1", + "src/main/daemon/daemon-endpoint-errors.ts": "83a553bd23a971f0f13da5420dfe11dc509f316cb9992153eda2cf09d8290aa7", + "src/main/daemon/daemon-tcc-attribution.ts": "c89d54b3ebe5e2f9c988b39052ddd5e496065515d70a340205b07ac43e7d6923", + "src/main/daemon/daemon-pid-identity.ts": "a64e4cfae8c80b7b83fec38b35f4c3f657e447414056feb435d374d04882a190", + "src/main/daemon/daemon-spawner.ts": "0e247aadfaafdf32f1a866f852ca348dbb481604a20a8990b948889cd9e39017", + "src/main/daemon/daemon-endpoint-ownership.ts": "b2eab15f782c142b756d5562b0246aa4026a7c97a51c3b30e094b607b15f3efc", + "src/main/daemon/daemon-pty-checkpoint-scheduler.ts": "2593cdbd3d0b48e45e04e7205970422bdc6c5ccd240d518961429f88751b50c1", + "src/main/daemon/daemon-durable-history-snapshot.ts": "35c3b071f3cd5f41e6d2f507f029057663983cf42ec211180fabb9f775cc5072", + "src/main/daemon/daemon-pid-file-parse.ts": "9a1acc477a9c76f19b7a2cc167889c74d48006e3538d0669bca5787afdaaad51", + "src/main/daemon/ndjson.ts": "d3df2c0a66ab9b234703d139aec8eea53e512eaf63bd85c97d46610e6de39e91", + "src/main/daemon/daemon-process-start-time.ts": "9cef6be893e1a760357b418863726b64684b7102e3d770dc66290756942372d3", + "src/main/daemon/daemon-process-identity-query.ts": "8f3f051ccbbb251ffb007e5157af73c217ae97f697828b9644c4d512b32afc9b", + "src/shared/main-process-ndjson-framer.ts": "cac5d9cdba42fc9f8340b2b815e6b36ff20e104d734a7ac4b8bca7136e6fa490", + "src/main/daemon/terminal-history-dimensions.ts": "bf8260412fe7e29a38063d2c7355ed63098d4de20a8ca8ebb5d1f9421b73d81b", + "src/main/daemon/cold-restore-replay-writer.ts": "0d9dee4459c802564b1b0ae38a5856d5b5cac98d96723700b08863f23faf05b5", + "src/main/daemon/daemon-respawn-throttle.ts": "976463ced65580f049e9d8b14585faa03dddc280c3cdb095531eb2040a477645", + "src/main/daemon/daemon-restore-scrollback-depth.ts": "ffcf76d820c9dd41289bd0af77d33d073a6c69a89e3ef18cf881b12a91ee6f0d", + "src/main/daemon/headless-emulator.ts": "b8bcb28dae2d9fb1b1361c9d32eb69357177943778bf56c2ce9848c82c9ba2a7", + "src/main/daemon/terminal-shell-lifecycle-scanner.ts": "2bc25f07183c727e5d4e9d5670bbcaa015e37f74abadba422b6bf8f040fdc04c", + "src/main/daemon/daemon-endpoint-probe.ts": "b27023ee842a68593ae657aa9d77cc5f125ee763839af0c1d8e4c8def35e2f52", + "src/main/daemon/daemon-pty-connection-lifecycle.ts": "8f705518dff40fc7fbf847aa310719f2950cd91ce6a2bf320be131ba48992eef", + "src/main/daemon/daemon-request-deadline.ts": "f6a55c176bb4aff63358517b70d69dc187fca406164621385aba4ce43f7f4a10", + "src/shared/process-output-field-scanner.ts": "03f89dcee7b9abc0c8c1a51423cb612ef936b8046d10a6b586fcea05e4a359a5", + "src/main/startup/startup-diagnostics.ts": "7318d5b3ead157615f85f4e5f7e921b64f2222fa01f1450c6c21e09c01b2089e", + "src/shared/terminal-scrollback-policy.ts": "7816c5a7540bbb3fbff61f7b3c4a89a8b331621f769b77bddd29abe06a752f17", + "src/main/daemon/daemon-pty-event-subscriptions.ts": "0a8f8de8430a674e61177563396d0d6322b8d60aa8f182ca8430e5477e4cd16e", + "src/main/daemon/daemon-audit-classifier.ts": "dcf4dff58e0cad4f0159bd1a1592693a920e4b6ef1d5acd92fd953813c5dfe9b", + "src/main/daemon/daemon-listener-registry.ts": "86966184f856fe1367cd3279bd09706e7935dcd62798e1cb8c07b35008281449", + "src/main/daemon/daemon-endpoint-incarnation.ts": "095bcacd5452ad9a893a27ace50b9072a823b291c17aed1901418242d57a2f07", + "src/shared/terminal-unicode-provider.ts": "eae524000d37c01ff9818ebe822dff4c12a3479b9a97370414a928cabb3d948f", + "src/main/daemon/xterm-env-polyfill.ts": "83a5c2f6386d27c9895699fd3dfd1c6bfdb38c6ae24948ea07d75e24c213e6e9", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1", + "src/shared/terminal-cursor-line-context.ts": "c02e769fb8e49111e63e52079b26feda2d47d70c5e56cf34a81244f2f893cae1", + "src/main/daemon/terminal-osc-cwd-title-scanner.ts": "77145544a185fd5a6007e415ed4c9b7f7bb3f6009c96391a72253f3550b2ea6d", + "src/main/daemon/headless-emulator-modes.ts": "acc05652aa78e3504f3d71250537ac5dbd61777fab9516ed69a22a5d11857807", + "src/main/daemon/terminal-mode-rehydrate-sequences.ts": "922d2533cc78adae2d93850507925426e17f456ea3cfb98bbc03b454b7d766ca", + "src/main/daemon/headless-osc-link-ranges.ts": "90232b09a6aed3f029952a6850f5c3e2037a395ca0c7d2750f497a7f38d1fdca", + "src/main/daemon/terminal-frame-restore-sequences.ts": "828285b5e5ca54ba59e6756d2fc6f89e468016ed3a7595775c6dedd08b2c57c7", + "src/main/daemon/startup-device-attributes-responder.ts": "de1092ba15ec03cdd2fb08b33cd5ba5a04f26b19616476e3c7f717e54d337b7b", + "src/shared/terminal-serialize-absolute-cursor.ts": "5e4566f8b4fb752a8bec0acf9e2e87a36d223b06760d53d911d42ed46b2100a9", + "src/shared/terminal-partial-escape-tail.ts": "0739b887bf650a81b6d596b5d2ec2e17d45254eaf3d95c7d008bd82b4af81d29", + "src/main/daemon/terminal-snapshot-ansi-buffers.ts": "8e95c7e000db648d64a582b06f75a21890c656f391ee996e583bdf7c1f6a13ee", + "src/main/daemon/terminal-view-attribute-responder.ts": "51fa68da93e150a2c15072999b63b3440fb5540b67d732020ca287123ae20fa7", + "src/shared/terminal-mode-reset-profiles.ts": "38028c60950d462412e6ebd9699ef0697c859f27c08da377a16b6c3e5da91049", + "src/main/daemon/osc7-uri-extraction.ts": "6be83d04eef8a38c65df5b8e20513526936a47d2e095e71a0df1269ad93567a7", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/main/daemon/daemon-incarnation-evidence.ts": "be9c8d27d040c0cfc1aca2593fa8a9c823ea6c496fa63bdadeafefa4fd71dabb", + "src/main/daemon/osc7-file-uri.ts": "a09f7b8f2bd73a7d547e5c5eecdaa62e23716a16da05fe082041e771ae8dbe9a", + "src/shared/terminal-view-attributes.ts": "f9a6f84532a49107daadf82cde6ac61949d3881d8ac42327f46a08a0c218e305", + "src/shared/terminal-escape-introducer.ts": "399406e19ea928d9905fe08a7aaa81807c436cda7f55e459293e94e868b43fc5", + "src/main/daemon/daemon-pty-session-inventory.ts": "ef12cd9c10adb536e6884db6e4a00e3915ea2286410228443228fc4972c0ecad", + "src/shared/wsl-paths.ts": "1d9dcf5a1ff6693c02ff60a6b074eb2ebb7e83bda12aceba7e0b208d410b6d1a", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/main/daemon/daemon-process-inspection.ts": "a5ece8904c6d99621b960d476dbeaddd104bd74e07f85bd2085ee26dc9d69683", + "src/main/agent-hooks/managed-hook-owner-identity.ts": "1c1d48647a1db09d1255b0816b267f3b8d2ec7b1ca267ffbef67b875eedcaf4c", + "src/main/daemon/daemon-incarnation-evidence-types.ts": "85b6135d13257b11ba3162941cd6443ca4e056fdf35091d4ea8f4109f48b1cb8", + "src/main/daemon/daemon-pty-process-inspection.ts": "4104b34ff7b3ef25989c51c391e532d8058b14921fd72216b8be36013f8725a8", + "src/shared/agent-session-host-authority.ts": "8c5952323d2766f136a3685eda51a439d363d1e2224da5516f3d9cabe34bd5d7", + "src/shared/claimed-agent-pty-owner-snapshot.ts": "82935b7bd08639e94ea181be0cb998dfef8c4960eb8d3a8563d6d1aa3156cca5", + "src/main/daemon/pty-session-id.ts": "1c8beaf069eb5e093e45a9489feffac60e8a5635e8f941722d2a3c3124b4f878", + "src/main/providers/pty-process-list-admission.ts": "6ba5c65c9552497aac0182b6ad8b38b4253d7834ee6853c0c6c1c252af7e2356", + "src/shared/claimed-agent-pty-owner.ts": "6c49c9b4649559a45149a3118a3560a7c724d9b06fd9b57182f5b880b4fd77c1", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/pty-session-id-format.ts": "42bb5177724f9dd38ae1814b38f795966536fd803d92364e0b5f9f49a8f8a49b", + "src/main/daemon/daemon-pty-buffer-snapshots.ts": "dd0e66898c3035be4c4fe41138a26f00f2d9d84e3999e328c77aa4cc2f29faf9", + "src/shared/protocol-version.ts": "053a152568959f5576050bc83743924772880c85ab6dfb7ce95c6c6acdb02954", + "src/shared/agent-session-resume.ts": "3dd5cd7ba4c764c848374b168e715a410974657a9b7e70e9d56b69a06254ce43", + "src/shared/stable-pane-id.ts": "0dfb20a173dc658229c46b7df4e36a443fee7089cffb79d76a74d8a92f41b884", + "src/shared/terminal-tab-id.ts": "3bf691ba305880a6d67f620d9ce671e5388e169c4783b1caf0d73fa589b5aa82", + "src/shared/foreground-process-evidence.ts": "e82342503c7fa83197ef1002f306d410ebed43e93c4dea86d6b8643ae87bf53c", + "src/shared/pty-incarnation.ts": "f232a871e24afd92b29e798601f23403a8cc082a6b89e009e5e9d99e6a1f82d5", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/workspace-scope.ts": "961b4e7d05f044aa58ac0f3e0ca9344d166f4787742836c855c06ba90dd71581", + "src/shared/terminal-surface-id.ts": "7093584672a541e22c0094bfb0257f6bd8f83d4b23e1a5b077132da011f8f9da", + "src/shared/remote-server-update.ts": "c7384b964a0bfaa1573cd00761c5e2d29143c591cb1b4cd6ee1b0db464110d12", + "src/shared/skill-install-capability.ts": "4dae209e014524ad9bd1f72f8366a78f80720546d36e19adbc299d3d7da1ff19", + "src/main/daemon/daemon-session-scrollback-window.ts": "038507c6193b5a18eb0e39276d83a1335a33dfb3b87dcf93286ed91a449f9a68", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "src/main/daemon/daemon-pty-session-control.ts": "5fd2e9ad493e0b89cf1c2d18d546e820bdcc863f6c4ba7e95b1dd77f5cea48b8", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/main/daemon/daemon-pty-applied-size.ts": "fc17c9636f0d6ce41ac2e4c45fc093d08a4ce29a9c4a0b6ed7202fc99fb821e0", + "src/main/providers/pty-default-cwd.ts": "ddb07acfd0ee4d45ff62416b013db9c7b2f1db3ef3d3b8120b941d32d71e2f6f", + "src/main/daemon/daemon-pty-lifecycle-errors.ts": "d706e04875eb4c08ae92eb0182eb6f0a4c07150a46b98f817e165dc1add92da1", + "src/main/daemon/daemon-pty-session-input.ts": "c1b08e0c51deebc21f2af16863051976a56565449e16f7aa2660b8d5c8fd6037", + "src/main/daemon/wsl-cold-restore-cwd.ts": "68a2309fc994799667a5955daa654a1bce3d42b4aa9bab28228f7732eb4d4200", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/main/daemon/daemon-pty-size.ts": "cb78f1d5e2cf142f86155595df62fd6f3ccd5f45aa11ed74068efc2de77ab741", + "src/main/providers/pty-path-safety.ts": "c6b67c9756ea26efea2641ec578ad92fdaad3d5f3f4b668aca6133f0f2ec5aa0", + "src/shared/pty-write-settlement.ts": "0726aba75f2ef127d41f47a0685fb42daab34fc139d5b9f727079aec88b702e6", + "src/main/daemon/daemon-pty-session-spawn.ts": "c14d3697aa509157cd0ccd64bb08dcc258901f3e64d8de85df1d1bbdfd1df114", + "src/main/providers/pty-write-unavailable-error.ts": "bb31714600152bc9e3f0e1be7c81fe50b9301b97f39318245607be3047db8e40", + "src/main/wsl-env.ts": "66eb01ec8a5593bcc0e64089a7024624deb009b19250d3d1e6eab2bbd315be52", + "src/shared/agent-process-recognition.ts": "2a93bfec55fabc7052bf1654d270131a61935c72976bfd3251585c7c1138a0a4", + "src/shared/codex-startup-delivery.ts": "8d2a47a0b56dc6ef1fa4a598973805898bd8e42f566130bd3d301d6cbc5733ad", + "src/main/daemon/session-shell-ready-barrier.ts": "a7ae62cc0ccce6eb8dfceb9437d3c1497d06588043bca49c559f3f6948f64109", + "src/main/daemon/daemon-adoption-telemetry-event.ts": "05ea98bcd32f606cc15c8f0cb9d66401ec2dc801f8e717ed6dd0e9c6911cbd2d", + "src/main/daemon/daemon-pty-spawn-result.ts": "4ef2cd27ee763d3542833e4ebf8afc4e0b75675689295a50e68ca6a79c2efe0e", + "src/main/daemon/shell-ready.ts": "8bea3210928c4efb35de5201a53b370eb473e32c7c404b39778efb1b7f779669", + "src/shared/shell-ready-marker-timing.ts": "4e7fcab88785ecc7d446831d23433c7031a01f534bcd53d5124531b2871857e2", + "src/main/daemon/terminal-history-seed-segments.ts": "e0d1bfc920a0df5a561a8ff6c8227ee352822c3d93f48f1015c271eaa6bafcf5", + "src/main/daemon/wsl-session-context.ts": "860dfd518182937b04e43d8d330066913799b00c4c2a5dbc04f81e7000e4718a", + "src/main/providers/local-pty-utils.ts": "d655643b42503ccfdab6436273557581b447f4b9f452dfa7649a959705744f25", + "src/main/terminal-history.ts": "f4ed054c6d478ae191f1379b614487076eb9ee35ec876197c2c20cc024de3013", + "src/shared/wsl-env.ts": "9f9a6bc39f8a3e4b245338652bf30721d8e3888734a40d7cab602dc792dccc88", + "src/main/telemetry/client.ts": "cd6fd4066a7fab559cc13aa4822ef80f0dba34a1e8211613344223b70db532d0", + "src/shared/app-environment.ts": "91bd2a883048a56f8c3966057a062bac75f48f9e1b5670d8d7958f8c7fe21140", + "src/shared/daemon-adoption-telemetry.ts": "7af34179b683b936ba248b7d92bc0f506642658673b0200e52bdfa4068729ed5", + "src/shared/daemon-lifecycle-telemetry.ts": "b3bcb90f98bbdcf98c76fb985de86b2ac5eb96a4b000c278f5a133bf79bc31dc", + "src/main/wsl.ts": "d888b67769461a7ba0a175db9775e2e2ab30601cbec53077a27351604a04882a", + "src/shared/worktree/id.ts": "d4da9e75db6f0d3301ef5f073d3136282dab4fc4fbd2978bdcdf0e142e7a9d30", + "src/shared/local-windows-terminal-runtime.ts": "6dfb40bf9bb1a73b358615ea82fd86abec10b1a170b7ef0771b9c574addd4e7c", + "src/main/terminal-history-id.ts": "58a0e50e220343925535c67911fb9e45f298916fad2163e6396ba9cc754baac0", + "src/main/terminal-history-paths.ts": "27148b37c6fa2f99540d18864013e8909aa86b65be7f92cd10b6a31a1d3d3e75", + "src/main/fish-history-session.ts": "0f746534888842d53ffc8f5565a6010b1c4a2f7bf5f750301c547f7e5450f8eb", + "src/main/worktree-history-file-path.ts": "b0b04151717e3fb245b17ad8ba140db32e0af55c7c740a14684e3c2729c5f0aa", + "src/main/daemon/daemon-attach-only-retirement.ts": "b883fe6f3647f1a95be9d3f0390653933d8bea79c21538d045fe2085d31bdbca", + "src/main/daemon/daemon-history-recovery-freeze.ts": "e629d2e2729e598293e64213464ca0fb45ba3f205b3f45a45781408f180262a0", + "src/main/daemon/daemon-pty-provider-sequence.ts": "6aa2e70fbc53a57d92b80f1b3e0f8b36ad33d7027191359eb674292d156416bb", + "src/main/daemon/daemon-pty-spawn-request.ts": "d3c2e9f8995c73df427dea36e0ea64508fef6fd5880616df9ad430e34d3acb3e", + "src/main/providers/working-directory-validation.ts": "197bcb7ab254d2bca8cdcc7d7f0214dedca825bbb0dc4f0d2dcbb41df4cec043", + "src/main/providers/macos-tcc-login-shell.ts": "3987924df6e455a41561926d50c3811394232f9dca68d96bbe67c152e57744ba", + "src/shared/node-pty-spawn-helper.ts": "4796707cd3d98c1b296a2b7ff6917b324de6ab830a7bb9cd14eb952bbd587c7d", + "src/shared/command-token-scanner.ts": "86eeb322b12bb3e5bc48edf5d85ec647c0c0ec24d3beb3526e1ae3c9aa7303a0", + "src/shared/agent-headless-command.ts": "10f84a57c243c7e9b13b9cd9419ff174cd0c8b20cf4783aeceb6cebbc52def87", + "src/shared/agent-node-entrypoint-identities.ts": "f2d58fcb086f30afbee3987a3b3ccbde566983e9f230a6da05f9c1d808ffa2d4", + "src/shared/tui-agent-config.ts": "ac7407900e6dafad72cc47aae171b169e8e1d7752d28caa0e09441d73a1fe1ad", + "src/main/shell-prompt-readiness-probe.ts": "8e871ed4a1d61569a29185bfa95ad4aa0a52ca127098088d3853bfcd5a8a2e73", + "src/main/daemon/post-ready-flush-gate.ts": "d53f2d436d768bbe4c4bfe1dd1442e84a031bc8078d3856c0361b40447936cac", + "src/main/shell-startup-output-scanner.ts": "cff0c8234de84ed34c1cbfd9013dbb10f5ac60efa01076ad3be2f80394248ac7", + "src/main/powershell-osc133-bootstrap.ts": "5a53aa934bbb50fba15dc2a0421b07910b09c76824a23ba224d8a4216ef9b446", + "src/main/daemon/daemon-shell-ready-marker.ts": "d128c88e65e7835864c67dc2c1e1a355c50111de77802930b06b350a0beb9a21", + "src/main/pty/codex-shell-launch-preflight.ts": "d80794841adace87db9a24fe3d73f873d5a1b9219ac6887962bace681677deca", + "src/main/shell-wrapper-file-writer.ts": "5ebfaf5c3356c651a359b6064cc8aac4a32a413b86548681b1e20370e198f232", + "src/main/daemon/daemon-shell-ready-wrapper-fileset.ts": "09cb0a1ff975c2299c46ea47c4ed79820aa93fc505592875db4add634249c55b", + "src/main/shell-wrapper-content-address.ts": "bd1ddf63b4de77fe8dabf533edfb0a14b81416e114f795978a1a46776ecea028", + "src/main/zsh-wrapper-dir-ownership.ts": "e6544e2d8d3348cc2a2e533c60a2bd1e91b6c8446fffa4dea24968218f9e2c9e", + "src/main/shell-templates.ts": "17122cebe3ea2b01462e645fa3ce335495734a3ab18f5fb7d015180060896d0a", + "src/main/shell-startup-features.ts": "1b418a6d759230950f320494fa70dddea030d3d1f29ea1fba05c238fc98211c7", + "src/shared/cross-platform-path.ts": "d72a91065f535824b17f2d3285f45f6d2f40ec2a2ac7ffb4ae17edaa4f9a97f3", + "src/main/telemetry/validator.ts": "39e028a8e431fe991ad6bf37acad7a0b0e9d7c174c07e898779b4a614bd12c99", + "src/main/telemetry/burst-cap.ts": "12a72a3f1582210fb8b8a81818f1471d62938359f97bdc2e5a2ce59571a3ce2c", + "src/main/telemetry/cohort-classifier.ts": "2d9aeafab4782fd350622d122d0381dda6b02272eda4db0d10b148bf2d5c933f", + "src/main/telemetry/consent.ts": "dd8b489ed5ed4af5cc912182ea1ec165f8e0056260a2f3b1e6725aa025027d39", + "src/main/shell-startup-identity-scanner.ts": "37aa9dc4a6ed70c5b52aeda7c4204cffc5b0838808dbe65df4266033628088f6", + "src/shared/print-mode-headless-command.ts": "4a632179d794718904989bfb00dabb7c741a0ae35d58585cf07f4aa746c36f7a", + "src/shared/priority-semaphore.ts": "509693d0132f2365bf1702bcbae7efc4bb0eceeb6ea235456f56a5f784da85a5", + "src/main/shell-ready-marker-scanner.ts": "a504be037d0ef1a19d3ce1ad608c279028132f0c91357c6dc1f2d5486d914a4d", + "src/shared/ante-headless-command.ts": "710a59e55ab1dfa32a33f7df9b4ecac77f85e387dc93e740d0bc678c37e61620", + "src/main/daemon/terminal-history-seed-chunks.ts": "a2616cd2815d92ba8dbe9ddad9d34adc08076a33765010ee18e82c4905adee3f", + "src/main/wsl-directory-probe-command.ts": "1db18370dff7e8656d27b105625dd03bdba51cb60099e90c0abb3252ec2ec246", + "src/main/daemon/daemon-pty-runtime-state.ts": "d0898287992c4a3acb14c9c4087c7b28cf11b13417f115d25b445d25d1404f19", + "src/main/providers/macos-login-session-pty-probe.ts": "260cb5a780287b6dea7c1eaa45e89b78fb9a5e1805c1f8c4bf2b17c5d952af63", + "src/main/wsl-availability.ts": "324c636f9779595b62fe1f8c98535ddc818b6d892195f1f28ec2ae0bd7ec1bcf", + "src/main/wsl-interop-spawn-directory.ts": "857a1253971cb0d4e7da5659c9ded80c4f0930c70b03e243ed858f8999f294cf", + "src/main/wsl-running-distro-cache.ts": "d8bcf954daa78cba331ef725bf1ab2c63a117cb3d0bb099edba6133ffeee386a", + "src/shared/child-process/run-process.ts": "ae4dad239273351ce61269cd7f7abb8325f3f620be600fa9c0ae760c1dd7cbad", + "src/main/wsl-distro-retry.ts": "d5711a29eb67bd49d9c512a5f35743921045b703ba27275cdcdc0d5aaf64e4be", + "src/main/wsl-distro-list-output.ts": "27f772c9dc451757f4397a4e47ca9c9e28c4067e50a41ceef846013ff43db7bc", + "src/main/line-editor-ready-output-scanner.ts": "c697380e4bf4677229f59346d7e69d7cc27ed03d163a2882885092e6eb703293", + "src/shared/pty-slave-line-discipline-echo.ts": "3df5bc2c7c45ffce766da5460ed5d888f463768d8d8287792cecf45e5f3618b4", + "src/shared/shell-process-readiness.ts": "9aeb7bcdc6e0ca1b70d62471a8ad7ac5f39fab966134cfd2397a8d7f850d82cd", + "src/shared/prime-agent-headless-command.ts": "0898cb5322ff91510e81c11ec9c7d4fc1cd6e850491092a78f79931722651ec3", + "src/main/daemon/daemon-zsh-shell-ready-wrapper-spec.ts": "502ef8934f77c6ec3ffe278cc4f8e644f0890bc4f2ac6360077e34c00bc3d58b", + "src/main/zsh-startup-wrapper-builder.ts": "eacff52ff47cebcd34914b020fa746d1e3720e3b7c8059013ffcf74d1cf3dc26", + "src/main/daemon/daemon-bash-shell-ready-rcfile.ts": "475bdef878015fb3531fe0f340d0f99d224146f0aa8adde406175a2e51e228be", + "src/main/bash-prompt-command-composition.ts": "1519e1b2f4d408c5e46ff7f7d9b3dc2187ac03ed6e99217ef2b5137158fa264d", + "src/main/pty/posix-shell-startup-command.ts": "f69611a77938cf13455f67728cf1a9fc1a67ff864919245787d659f3c1c6055c", + "src/main/cli/bundled-cli-launcher-path.ts": "d18971de3491d0f678c99ab3fca72dd71c5b3e635bf258b7c8d1ba4584e358c4", + "src/shared/powershell-command-encoding.ts": "405d7aa5cf55f0053a6ae84f92335446dde6c316f782761be5ce1c24967046a9", + "src/shared/orca-cli-command-name.ts": "c90b037ec7a6143770a40dd907bf9d053fb2cede21128b60b78a78671c0780c0", + "src/main/pty/omp-shell-wrapper.ts": "fcf15117265833933064f0315ce1b02473d8f5618efed09c0d3f8d4872395fcf", + "src/shared/telemetry-events.ts": "40b800fdb343cd66801613dddfcacebc148adf864ceb65e6f70e250136e9f4b6", + "src/main/daemon/daemon-audit-eligibility-event.ts": "11755c26094c81a82c72027eb5b96b97fbd686bca4f32c9fbc9ba1758fbfa137", + "src/main/daemon/client.ts": "e7698849f62c51bd36bf6789423dcd3f9194d979fc143dd267f0c1b1de1ddf14", + "src/main/daemon/daemon-checkpoint-session-queue.ts": "f4cc3809309c57125955ee8a7439df8f8000bfca452015c5bffad3ceea5fdf7b", + "src/main/daemon/cold-restore-payload-cache.ts": "6e97bb387ec9b00619226685934126a90e4572720de3ed6a16de0cf8f43ceb77", + "src/main/daemon/history-manager.ts": "7b0a99ecbf735ea3bf6480af6463dc15a35ea6dc27b3b0e8d07b73a787ad0f5c", + "src/shared/process-table-snapshot.ts": "fe423af1745925a52b5d205a3cb20325220c39996e1c065d56fe2af1e69182e9", + "src/main/daemon/history-reader.ts": "8579629f0d023bad3d652661c6f767ad5be767a2759c2978b114645df0814a3d", + "src/shared/child-process/spawn-resolution.ts": "8c62c3c14d6f110f25bab23e3f4b2633a68e2f36252bd3aac68cffd7b2e274dc", + "src/shared/child-process/process-tree-termination.ts": "49f001659c872e23040fe5bab2dd387f2589178f5bc3ce9361373303d23ff120", + "src/shared/child-process/bounded-output-sink.ts": "7dbab9fc70dac8e5d19dd03d743b55609092b45ca13423e2dc80a69cf7fc4865", + "src/shared/child-process/process-spec.ts": "97e52b5e320da1eed4cfb2c0b73a88696b62f355eb4b5fe0df5389764afb8289", + "src/shared/child-process/child-termination-reporter.ts": "d91ff09b26c9a23e26c2a2f92b129879cd6d0bddea14cd3bcac5d4489fecfca0", + "src/shared/wsl-login-shell-command.ts": "69445d4cb6eae151ef28ed4fe320774bad9c46577f8a2263c14a14ecfdb28075", + "src/shared/telemetry-event-classification.ts": "0a04ffab3f99083e2a07e254040633519771450303e6dc803cd46ac074c97a4e", + "src/shared/telemetry-property-schemas.ts": "3b09098052708589b2954189e19d595668b4e3d4ccb09b1de75681839d7c72bd", + "src/shared/telemetry-app-event-schemas.ts": "5dc7d68e3a235db3755ae71381a8b01e3584f21a80b6de48a6b4e7538f1c0831", + "src/shared/telemetry-event-registry.ts": "fd2782c8606fac43241fa96e0999fd651047434ffac68688dd009f060a315be9", + "src/shared/telemetry-daemon-event-schemas.ts": "0566f2763169d68a63887bcabf15e3f6a92237191caeaa01276022c647c982ac", + "src/shared/child-process/process-tree-kill-gate.ts": "62848f063785e15cb0231deb622b043e81484ed3040c612afdc4f98fa2db06ab", + "src/main/daemon/history-paths.ts": "a565f91fb2def34aa2c063d0089fa9b491c5aabd7736886ba9e4d2bb31418b63", + "src/main/daemon/daemon-client-hello-handshake.ts": "532be287024d5fa8374d61af5ed488622b2f7a8530642a2120a4993caa14cc73", + "src/main/daemon/daemon-client-pending-requests.ts": "4fa6740fc377c898493809f7469b98c37062c059aaef38bd07dcd6f5e9bccb9b", + "src/main/daemon/daemon-client-socket-connect.ts": "958e918424e5ea6d9ed4f8d85e1d07c46977e074ea2f2f880dc6b1ac5c927c76", + "src/main/daemon/daemon-client-listener-registry.ts": "ff74d77bd915b4ae424054be740322e17e46bcc64316057c264eed1010c1a50b", + "src/main/daemon/daemon-client-ndjson-readers.ts": "3333a83f0f75a05867083c6890c564e4bdbd0df820313d646944c55ba04056ee", + "src/main/daemon/daemon-client-notify-settlement.ts": "7ce27ff7c79fb5027b884328505f0b792c4a039886deae12371a4b5e4594bf25", + "src/shared/child-process/windows-command-line.ts": "4bf94aa90ee63d965bc6e1198ddafa4a715c05d78be979025d17dc59d90557ca", + "src/shared/child-process/windows-cmd-shim-resolution.ts": "5044e3a9140a19c8bb713044de655b611244659a754f32e5a5df7f519df40eb3", + "src/main/daemon/daemon-client-rpc-request.ts": "c846c4e7cb1a5eaa3266e3f3779dbd62c7f60a0c783c55d2af80c42d8468db7b", + "src/main/daemon/terminal-history-log.ts": "8d9ec783257c1f7bd7380491038b499ecaca00f57d99047806cba9d6ab816bcb", + "src/main/daemon/terminal-history-checkpoint-reader.ts": "d69e6e765d9b318ecbdeca27fc295e57e678d47e1312b77f0b2900b1b727b8d1", + "src/main/daemon/terminal-history-file-reader.ts": "923c9e0c5e803c2f3ea25969341c76525ece822ddd3cea4d96c5176c438b92ae", + "src/main/daemon/terminal-history-session-tombstone.ts": "ed4ced675f275e16b5a1a142e91b5e33e10a4a35885ba2dd8a7e5a90e4d8ebe6", + "src/main/daemon/terminal-history-recovery-quarantine.ts": "65e37d29a84d8ebaf43e78e13b94004c2ca9a0801c49a843642ff84928837c85", + "src/main/daemon/terminal-history-cold-restore-info.ts": "5f44e06414227fa7148c0a88784aa5146b23ab36719db9d293432ebde63b0680", + "src/main/daemon/terminal-history-file-limits.ts": "21185a571e0d326472554dae3b49bc578834d00432a2bf50f044a4d3da1e1a3a", + "src/main/daemon/terminal-history-restorable-retention.ts": "1bfc897132026875086cb99e9d2d5df73db793cd6cef916706934b215bfd7b7d", + "src/main/daemon/terminal-history-legacy-scrollback-restore.ts": "5af2cb2998362feccbe1025e019aef6c5ebaf1d4ae9d9e4c6ef527033f1fc441", + "src/main/daemon/terminal-history-metadata.ts": "d273daac2c06d140b8a75a1d43382025f04b430708af57fd359623ee506ce084", + "src/main/daemon/terminal-history-mutation-tracker.ts": "fa7a88fb2bd9461b12cd97c60c1383f9c51b4d0e445b9d8852dbeabf136f548e", + "src/main/daemon/terminal-history-recovery-freezes.ts": "c1dd816085442d69b556a590196bed679aaf02f14d919ca7c62742d382591f0a", + "src/main/daemon/terminal-history-session-files.ts": "d8b0c2ac3e41a267f2a85aa11571e66807e9204613881433ddeb2a5beca217fb", + "src/main/daemon/terminal-history-session-writer.ts": "50eb181f8f13355e9ebf5c3dd1bc004ec10b3eba51bd74368a75421634562856", + "src/main/daemon/daemon-private-file-modes.ts": "7dbd0693d254e8ec2bf4f73499ecc8eca560ef1d440fe0026cff910e58bf0615", + "src/shared/star-nag-telemetry.ts": "dd3e57bc92a680ca78c521d3e503ec42220c7c9bef6970523cb804e79a7f0f7b", + "src/shared/telemetry-feature-education-event-schemas.ts": "51d0e9260f7945aa8efe28c0db73b935ff662da03567e6867f1c5470e24cd2fe", + "src/shared/telemetry-native-feature-event-schemas.ts": "407d1e75d18cde42ef430689af476d122b2f3f6a08b4c406454f1b0bd66c453d", + "src/shared/telemetry-onboarding-event-schemas.ts": "9e74c1de5cb4cc07b5f6cd0ec3e03fc3c183ef16e862e2485217057397b28756", + "src/shared/telemetry-repository-event-schemas.ts": "d8cc2d46954a41accc3b5e5cecefc6be2410e60e416411e5be8178cd13cc2572", + "src/shared/daemon-audit-eligibility.ts": "54322c582a2ffc880ace5b0eefd3f44b8110e038de9603207601d9ef35979446", + "src/shared/feature-wall-tour-depth.ts": "93614de5dacfa1fd084dffbe56d8a6a5a20097dc6fbc6c39eabd7f49e60e46cb", + "src/shared/setup-script-import-providers.ts": "f152cefab4922d992633da4d950b319b9e61862c3835d6fe88ed0e98edcc3f5a", + "src/shared/workspace-source.ts": "78c66dc7361ade7984de2d7fb5afa879eaade2a2f289e42cd78e53eee07d5b29", + "src/shared/gh-star-source.ts": "92d6db38e5f5881d9c522fa0e85c9f4b757e41cc92517e5d642c8795eb4310fe", + "src/shared/feature-interactions.ts": "d0f06fea1a436f77584b8bc3c93a944ee0cc1504cb0b12fc08cf49eb2d6dec7a", + "src/shared/agent-hook-types.ts": "dc64ba21ad8dd32c4a9a43eacb2c66d19c6dfe8dc6d77b2656f008b70597b78b", + "src/main/daemon/node-pty-error-hints.ts": "0595cf09081e0ed369078d1698093f02f3c23e5814e3d884b5083e70e8bd440a", + "src/shared/terminal-owner.ts": "60e3c07e99b8d2813f2241a8fc5ab8d1a11229b23b32aa53fb67154052c92309", + "src/shared/terminal-osc-link-ranges.ts": "bc3c39fd44489a27669225474ab9f52aaae32988ed80b26aab69e1ad0cb3d390", + "src/shared/node-bounded-file-reader.ts": "2cffa451b72d059226e0c2d4b6d2a5203cb8ddafb358d5e828dcd3436d197ecb", + "src/main/daemon/terminal-checkpoint-serializer.ts": "dfd5f0f3b72f58ad9d6b9df712dc1318b8e04dc7ec7327ccf714c0e7bfde1375", + "src/main/host-tree-removal.ts": "ba8ea5e3503bf30eddf70efc68bdd6a1d9b864734ac67cf21f83b95f4f862c97", + "src/shared/nested-repo-telemetry.ts": "fdcb4ce4565e7d71f6833099c3725a7bb2fcf27ef6d2089ad77ba588ad12efe7", + "src/shared/feature-interaction-categories.ts": "320daec20570a0d5cae909bd4eec1d8b35a460a54aee480ddfb464fc1603d27d", + "src/shared/feature-interaction-catalog.ts": "0015a3cfc624d9dd800d54dba6cc420e4e7505131cedc3314f38c1ed03eeda80", + "src/shared/feature-interaction-usage-buckets.ts": "5d4ac4b615d4af1dbb3057d3373d33fdf25c4bef15f93a93884c3e660cfcc8ff", + "src/shared/feature-wall-telemetry.ts": "1ae90c3ee862a109493eb88ff3690a124d05e3c5cdaac331b818610925ec5693", + "src/shared/feature-wall-setup-steps.ts": "e917ed94cf96ea99cce9f537bdd37bd0ddb7cbc6172d8f67aac9efe11379160d", + "src/shared/telemetry-onboarding-foundation-schemas.ts": "32772dc9111ad9e72f83322ee54a4f090b49cd14592f95b129be61b6469dc0d3", + "src/shared/feature-education-telemetry.ts": "7330076fabc31fc2878b79caa7b2b1cc901d6f99387e7ec846a85942f08b4d00", + "src/shared/windows-transient-lock-removal.ts": "dbfbc53d044d4bb76d1c5378a1011dff69b88c1ba042b23ebd1b0057153868f2", + "src/main/asar-transparent-fs.ts": "48b031ef8f72545c5f8f0d4cf6fb80e168a38315e83103ff00e4392c9cbdd2ee" + } +} diff --git a/docs/audits/daemon-shared-owner-incarnation-retention/sources.cjs b/docs/audits/daemon-shared-owner-incarnation-retention/sources.cjs new file mode 100644 index 00000000000..f1a0346cfd9 --- /dev/null +++ b/docs/audits/daemon-shared-owner-incarnation-retention/sources.cjs @@ -0,0 +1,100 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const Module = require('node:module') +const { createHash } = require('node:crypto') +const { build } = require('esbuild') +const { applyPatch, parsePatch, reversePatch } = require('diff') + +const root = path.resolve(__dirname, '../../..') +const canonicalLf = (value) => value.replaceAll('\r\n', '\n') +const read = (file) => canonicalLf(fs.readFileSync(file, 'utf8')) +const sha = (value) => createHash('sha256').update(value).digest('hex') +const versions = JSON.parse(read(path.join(__dirname, 'source-versions.json'))) +const relative = (file) => path.relative(root, file).split(path.sep).join('/') + +function loadSources(readSource = read) { + const fixed = canonicalLf(readSource(path.join(root, versions.sourcePath))) + assert.equal(sha(fixed), versions.fixedSha256) + const patches = parsePatch(canonicalLf(readSource(path.join(__dirname, 'fix.patch')))) + assert.equal(patches.length, 1) + assert.equal(patches[0].newFileName, `b/${versions.sourcePath}`) + const before = applyPatch(fixed, reversePatch(patches[0])) + assert.notEqual(before, false) + assert.equal(sha(before), versions.baselineSha256) + return { before, fixed } +} + +async function load(phase, readSource = read) { + assert.ok(['before', 'fixed'].includes(phase)) + const checked = loadSources(readSource) + const evaluatedSources = {} + const provenanceSources = {} + const built = await build({ + stdin: { + contents: [ + "export { DaemonPtyAdapter } from './src/main/daemon/daemon-pty-adapter'", + "export { DegradedDaemonPtyProvider } from './src/main/daemon/degraded-daemon-pty-provider'" + ].join('\n'), + resolveDir: root, + loader: 'ts' + }, + absWorkingDir: root, + bundle: true, + platform: 'node', + format: 'cjs', + packages: 'external', + write: false, + plugins: [ + { + name: 'hash-fenced-owner-incarnation-sources', + setup(builder) { + builder.onResolve({ filter: /^\./ }, (args) => { + const base = path.resolve(args.resolveDir, args.path) + for (const file of [base, `${base}.ts`, path.join(base, 'index.ts')]) { + const key = relative(file) + if (key === versions.sourcePath || Object.hasOwn(versions.dependencies, key)) { + return { path: file } + } + } + return undefined + }) + builder.onLoad({ filter: /\.ts$/ }, ({ path: file }) => { + const key = relative(file) + let contents = canonicalLf(readSource(file)) + const actual = sha(contents) + provenanceSources[key] = actual + if (key === versions.sourcePath) { + assert.equal(actual, versions.fixedSha256) + contents = checked[phase] + } else { + assert.ok(versions.dependencies[key]?.includes(actual), `Dependency drift: ${key}`) + } + evaluatedSources[key] = sha(contents) + return { contents, loader: 'ts' } + }) + } + } + ] + }) + const evaluatedKeys = Object.keys(evaluatedSources).sort() + const recognizedGraph = [versions.workingEvaluated, versions.publicationEvaluated].some( + (known) => JSON.stringify(Object.keys(known).sort()) === JSON.stringify(evaluatedKeys) + ) + assert.equal(recognizedGraph, true, 'Unreviewed evaluated module graph') + const filename = path.join(__dirname, `${phase}-bundle.cjs`) + const loaded = new Module(filename, module) + loaded.filename = filename + loaded.paths = Module._nodeModulePaths(root) + loaded._compile(built.outputFiles[0].text, filename) + return { + api: loaded.exports, + provenance: { + evaluatedSources, + provenanceSources, + bundleSha256: sha(built.outputFiles[0].text) + } + } +} + +module.exports = { load, loadSources, read, sha, root, versions } diff --git a/docs/audits/daemon-shared-owner-incarnation-retention/validation.json b/docs/audits/daemon-shared-owner-incarnation-retention/validation.json new file mode 100644 index 00000000000..f9e7ea8a662 --- /dev/null +++ b/docs/audits/daemon-shared-owner-incarnation-retention/validation.json @@ -0,0 +1,88 @@ +{ + "backgroundLaunch": "All tests and proofs used ORCA_BACKGROUND_LAUNCH=1; Electron additionally used ELECTRON_RUN_AS_NODE=1. No application, native PTY, socket or network.", + "fixedTests": { + "passed": 54, + "failed": 0, + "files": 3, + "newTests": 4, + "config": "config/vitest.config.ts" + }, + "baselineOverlay": { + "passed": 53, + "failed": 1, + "config": "docs/audits/daemon-shared-owner-incarnation-retention/before.config.mjs", + "intendedFailure": "releases both private indexes after repeated authenticated daemon replacements", + "actualLivenessKeys": ["retired-0", "legacy-live"], + "expectedLivenessKeys": ["legacy-live"] + }, + "portableProofs": { + "reports": [ + "node-results.json", + "electron-results.json", + "publication-node-results.json", + "publication-electron-results.json" + ], + "phasesPerReport": ["before", "fixed"], + "replacementCyclesPerPhase": 32, + "afterLegacyExit": { + "before": { + "sharedRoutes": 0, + "attachEntries": 0, + "livenessEntries": 32 + }, + "fixed": { + "sharedRoutes": 0, + "attachEntries": 0, + "livenessEntries": 0 + } + }, + "workingEvaluatedModules": 276, + "publicationEvaluatedModules": 274, + "crlfSourceAndPatchReads": 2, + "controls": [ + "unchanged authenticated identity", + "other live provider", + "ordinary exit", + "same-ID successor", + "matching direct attach without inventory", + "authoritative incarnation mismatch refusal" + ] + }, + "typechecks": { + "node": "Passed full pnpm tc:node." + }, + "fullPublicationQuality": { + "paths": [ + "src/main/daemon/daemon-session-owner-resolution.ts", + "src/main/daemon/daemon-shared-owner-incarnation-retention.test.ts", + "docs/audits/daemon-shared-owner-incarnation-retention/sources.cjs", + "docs/audits/daemon-shared-owner-incarnation-retention/scenario.cjs", + "docs/audits/daemon-shared-owner-incarnation-retention/reproduce.cjs", + "docs/audits/daemon-shared-owner-incarnation-retention/before.config.mjs" + ], + "scans": [ + "default rules and unused suppression", + "casting", + "type-aware quality", + "React Doctor", + "design system", + "default type-aware rules" + ], + "result": "All six full-file scans passed with --no-ignore --deny-warnings, including CJS/MJS artifact files." + }, + "changedQuality": { + "base": "6ab6802df90553d2a26e9a4c7a519ae11d4b5e09", + "result": "Passed all changed-code scans plus SAFETY rationale across two changed source files. Artifacts separately covered by explicit full-file scans." + }, + "productHashes": [ + { + "path": "src/main/daemon/daemon-session-owner-resolution.ts", + "sha256": "8f62a181a398dc91d20d269a19e3266e199191486fddfee6dcaf3b2a570c76eb" + }, + { + "path": "src/main/daemon/daemon-shared-owner-incarnation-retention.test.ts", + "sha256": "b88680e374c054143c344444b98e80368c05736add90698f9e540d2a4420265f" + } + ], + "limits": "Entry-count retention proof, no byte/RSS/OOM/incident claim. Finite inert transport replies. Named main source overlays use working external dependencies; v1.4.198 checked core source parity only." +} diff --git a/src/main/daemon/daemon-session-owner-resolution.ts b/src/main/daemon/daemon-session-owner-resolution.ts index 1906ebfb35b..47fe531ec7f 100644 --- a/src/main/daemon/daemon-session-owner-resolution.ts +++ b/src/main/daemon/daemon-session-owner-resolution.ts @@ -52,6 +52,12 @@ export class DaemonSessionOwnerResolver { this.routeIncarnations.delete(sessionId) } } + // Another resolver may already have removed this provider's shared routes. + for (const sessionId of this.routeIncarnations.keys()) { + if (!this.routes.has(sessionId)) { + this.routeIncarnations.delete(sessionId) + } + } } async spawnAttachOnly(opts: PtySpawnOptions & { sessionId: string }): Promise { diff --git a/src/main/daemon/daemon-shared-owner-incarnation-retention.test.ts b/src/main/daemon/daemon-shared-owner-incarnation-retention.test.ts new file mode 100644 index 00000000000..2fb980f1870 --- /dev/null +++ b/src/main/daemon/daemon-shared-owner-incarnation-retention.test.ts @@ -0,0 +1,157 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { join } from 'node:path' +import { tmpdir } from 'node:os' +import { DaemonPtyAdapter } from './daemon-pty-adapter' +import { DegradedDaemonPtyProvider } from './degraded-daemon-pty-provider' +import { LocalPtyProvider } from '../providers/local-pty-provider' +import type { PtyProcessInfo } from '../providers/types' +import { TerminalSessionOwnerUnverifiedError } from './daemon-errors' + +const cleanups: (() => void)[] = [] + +afterEach(() => { + for (const cleanup of cleanups.splice(0)) { + cleanup() + } + vi.restoreAllMocks() +}) + +function adapterFixture(label: string, pid: number) { + const adapter = new DaemonPtyAdapter({ + socketPath: join(tmpdir(), `unused-${label}.sock`), + tokenPath: join(tmpdir(), `unused-${label}.token`) + }) + const client = adapter['client'] + vi.spyOn(client, 'ensureConnected').mockResolvedValue() + vi.spyOn(client, 'ensureConnectedWithin').mockResolvedValue() + const identity = vi.spyOn(client, 'getDaemonIdentity') + const request = vi.spyOn(client, 'request').mockResolvedValue({ sessions: [] }) + const spawn = vi.spyOn(adapter, 'spawn').mockImplementation(async (opts) => ({ + id: opts.sessionId ?? 'unexpected-fresh-spawn', + incarnationId: opts.expectedIncarnationId, + isReattach: true + })) + return { + adapter, + request, + spawn, + setProcesses(processes: PtyProcessInfo[]) { + request.mockResolvedValue({ + sessions: processes.map((process) => ({ + sessionId: process.id, + incarnationId: process.incarnationId, + cwd: process.cwd, + isAlive: true + })) + }) + }, + async publishIdentity(generation: number) { + identity.mockReturnValue({ + pid, + startedAtMs: generation + 1, + launchNonce: label + generation + }) + await adapter.establishLifecycleLease() + } + } +} + +async function fixture() { + const current = adapterFixture('current', 999_999_997) + const legacy = adapterFixture('legacy', 999_999_998) + const fallback = new LocalPtyProvider() + vi.spyOn(fallback, 'listProcesses').mockResolvedValue([]) + const provider = new DegradedDaemonPtyProvider({ + current: current.adapter, + legacy: [legacy.adapter], + fallback + }) + cleanups.push(() => provider.dispose()) + await current.publishIdentity(0) + await legacy.publishIdentity(0) + const recovery = provider['ownerRecovery'] + return { current, legacy, provider, recovery } +} + +function processInfo(id: string, incarnationId: string): PtyProcessInfo { + return { id, incarnationId, cwd: '', title: 'shell' } +} + +describe('shared daemon owner incarnation retirement', () => { + it('releases both private indexes after repeated authenticated daemon replacements', async () => { + const { current, legacy, provider, recovery } = await fixture() + legacy.setProcesses([processInfo('legacy-live', 'legacy-incarnation')]) + for (let generation = 0; generation < 16; generation++) { + const id = `retired-${generation}` + current.setProcesses([processInfo(id, `incarnation-${generation}`)]) + await provider.discoverDaemonSessions() + await expect(provider.probePtyLiveness(`unmapped-${generation}`)).resolves.toBe(false) + expect(recovery['livenessResolver']['routeIncarnations'].get(id)).toBe( + `incarnation-${generation}` + ) + current.setProcesses([]) + await current.publishIdentity(generation + 1) + expect([...provider['sessionProviders'].keys()]).toEqual(['legacy-live']) + expect([...recovery['attachResolver']['routeIncarnations'].keys()]).toEqual(['legacy-live']) + expect([...recovery['livenessResolver']['routeIncarnations'].keys()]).toEqual(['legacy-live']) + } + }) + + it('preserves the same session ID after another provider publishes its successor', async () => { + const { current, legacy, provider, recovery } = await fixture() + current.setProcesses([processInfo('same-id', 'old-incarnation')]) + await provider.discoverDaemonSessions() + await provider.probePtyLiveness('unmapped-old') + current.setProcesses([]) + legacy.setProcesses([processInfo('same-id', 'new-incarnation')]) + await provider.discoverDaemonSessions() + await provider.probePtyLiveness('unmapped-new') + await current.publishIdentity(1) + expect(provider['sessionProviders'].get('same-id')).toBe(legacy.adapter) + expect(recovery['attachResolver']['routeIncarnations'].get('same-id')).toBe('new-incarnation') + expect(recovery['livenessResolver']['routeIncarnations'].get('same-id')).toBe('new-incarnation') + }) + + it('keeps matching-incarnation direct attach without consulting another inventory', async () => { + const { current, legacy, provider } = await fixture() + legacy.setProcesses([processInfo('live', 'live-incarnation')]) + await provider.discoverDaemonSessions() + await provider.probePtyLiveness('unmapped') + await current.publishIdentity(1) + current.request.mockClear() + legacy.request.mockClear() + await expect( + provider.spawn({ + sessionId: 'live', + attachOnly: true, + cols: 80, + rows: 24, + expectedIncarnationId: 'live-incarnation', + expectedIncarnationIsAuthoritative: true + }) + ).resolves.toMatchObject({ id: 'live', incarnationId: 'live-incarnation', isReattach: true }) + expect(current.request).not.toHaveBeenCalled() + expect(legacy.request).not.toHaveBeenCalled() + expect(current.spawn).not.toHaveBeenCalled() + expect(legacy.spawn).toHaveBeenCalledOnce() + }) + + it('retains authoritative incarnation mismatch refusal after another daemon changes', async () => { + const { current, legacy, provider } = await fixture() + legacy.setProcesses([processInfo('live', 'current-incarnation')]) + await provider.discoverDaemonSessions() + await current.publishIdentity(1) + await expect( + provider.spawn({ + sessionId: 'live', + attachOnly: true, + cols: 80, + rows: 24, + expectedIncarnationId: 'retired-incarnation', + expectedIncarnationIsAuthoritative: true + }) + ).rejects.toBeInstanceOf(TerminalSessionOwnerUnverifiedError) + expect(current.spawn).not.toHaveBeenCalled() + expect(legacy.spawn).not.toHaveBeenCalled() + }) +}) From 78289d8ebe5584508750617caed011f0eacd16c6 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:12:34 -0700 Subject: [PATCH 061/168] fix: release settled browser results after dispatcher close (#21164) Co-authored-by: m4air --- .../browser-closed-result-retention/README.md | 77 ++++ .../before-electron-results.json | 138 ++++++ .../before-node-results.json | 138 ++++++ .../browser-closed-result-retention/fix.patch | 16 + .../fixed-electron-results.json | 138 ++++++ .../fixed-node-results.json | 138 ++++++ .../scenario.test.mjs | 330 ++++++++++++++ .../source-versions.json | 405 ++++++++++++++++++ .../sources.cjs | 42 ++ .../validation.json | 228 ++++++++++ .../vitest.config.mjs | 30 ++ .../browser-client-host-command-dispatcher.ts | 3 +- ...rowser-client-host-command-result-cache.ts | 6 +- ...wser-client-host-command-retention.test.ts | 201 +++++++++ 14 files changed, 1888 insertions(+), 2 deletions(-) create mode 100644 docs/audits/browser-closed-result-retention/README.md create mode 100644 docs/audits/browser-closed-result-retention/before-electron-results.json create mode 100644 docs/audits/browser-closed-result-retention/before-node-results.json create mode 100644 docs/audits/browser-closed-result-retention/fix.patch create mode 100644 docs/audits/browser-closed-result-retention/fixed-electron-results.json create mode 100644 docs/audits/browser-closed-result-retention/fixed-node-results.json create mode 100644 docs/audits/browser-closed-result-retention/scenario.test.mjs create mode 100644 docs/audits/browser-closed-result-retention/source-versions.json create mode 100644 docs/audits/browser-closed-result-retention/sources.cjs create mode 100644 docs/audits/browser-closed-result-retention/validation.json create mode 100644 docs/audits/browser-closed-result-retention/vitest.config.mjs create mode 100644 src/main/browser/browser-client-host-command-retention.test.ts diff --git a/docs/audits/browser-closed-result-retention/README.md b/docs/audits/browser-closed-result-retention/README.md new file mode 100644 index 00000000000..4658aedfbb0 --- /dev/null +++ b/docs/audits/browser-closed-result-retention/README.md @@ -0,0 +1,77 @@ +# Closed browser dispatcher retains completed results behind pending native work + +Before the fix, completed command results stayed in a closed browser dispatcher until its final handler settled. The fix releases settled cache records at close and drops newly settled records while closed. Pending native handlers, page authority, and executor teardown keep their existing lifetime. + +## Actual paths and bounds + +Source references in this section describe the hash-fenced baseline. `BrowserClientHostCommandDispatcher.dispatch` refuses every command once closed, before authority or duplicate lookup (`browser-client-host-command-dispatcher.ts:77–79`). `close` aborts active work and cancels queued work, but retains its pages and cached completed records when the join returns false (`:156–179`). `finishHandler` clears those owners only after the last native handler settles (`:268–272`). A newly settled cancellation record is also cached while a sibling remains active (`:297–302`). + +`BrowserClientHostCommandResultCache.clear` drops only its record-to-page index. PageState.records and PageState.sequencesByCommandId also own the records; clearing just that index does not release result graphs. Existing `releasePage` uses exact settled-record eviction to remove both indexes (`browser-client-host-command-result-cache.ts:27–55`). + +Defaults (`browser-client-host-command-state.ts:7–13`) are 256 pages, 256 active commands, 8 concurrent handlers, 32 queued/page, 64 cached results/page, 1,024 cached results total, and a 5,000 ms close/retirement join. Automation result schema allows at most 768 KiB of JSON-serialized value (`browser-client-automation-protocol.ts:5,89–99,116–131`). These are count/serialized-value limits, not a guaranteed heap/RSS size. The audit uses 32 tiny results, one native wait, and one canceled tiny queued input; it does not allocate near those maxima. + +Production composition calls dispatcher close via `PairedRuntimeBrowserClientHost.closeHost` (`paired-runtime-browser-client-host.ts:165–180`). If close times out, actual `closeBrowserClientHostComposition` defers executor close behind `whenHandlersSettled` (`paired-runtime-browser-client-host-teardown.ts:37–56`). Keeping handler/page/native authority alive is intentional. Completed results cannot serve new or duplicate closed requests and need not share that lifetime. + +## Ordinary handler time boundaries + +- The navigation command checks cancellation before starting, then calls `routeWebContents.navigateGuest` (`browser-client-page-command-execution.ts:20–40`). The actual registry delegates to `navigateBrowserRouteGuest`, which awaits native `guest.loadURL` (`browser-route-guest-lifecycle.ts:99–123`) without adding a JS deadline or taking the AbortSignal. Native completion/rejection remains its settlement owner. +- Automation checks cancellation before execution, registers the exact guest, and forwards the signal into RPC (`browser-client-page-automation-runtime.ts:42–57`; startup `main-process-ready-runtime.ts:61–77`). Core handlers such as browser.snapshot destructure runtime and call its method without observing that signal (`runtime/rpc/methods/browser-core.ts:39–43`). +- The ordinary agent-browser helper execution has a 90-second default subprocess timeout (`agent-browser-bridge-types.ts:6`; `agent-browser-bridge-raw-process.ts:20–36`), with overrides for some operations. The agent-bridge embedded goto wrapper separately has a 30-second navigation timeout. Those deadlines are not a universal bound on all handler phases, and the direct route navigate path above does not use that wrapper. + +The condition is a handler that outlives the dispatcher's five-second join. No affected-host occurrence, natural indefinite stall, or incident attribution has been established. + +## Bounded actual-source proof + +`scenario.test.mjs` uses the actual dispatcher, page executor, automation runtime, browser.snapshot RPC descriptor, native-navigation wrapper, and composition teardown function. Existing page-executor harness supplies renderer/session/route ports. The runtime's browserSnapshot/native loadURL are small controlled ports; no Electron window, OS child, real web request, or network is used. Logger/mock result arrays do not own the produced payloads: the automation output and dispatcher handler are plain functions, and each completed value is observed only through WeakRef after the helper returns. + +Both Node 26.6 and Electron 43.7 / Node 24.21 pass all 4 cases before and after the two-file fix. A 15 ms join override keeps the proof bounded; the native port is explicitly resolved in finally and all native custody eventually settles. + +| Observation | Before | Fixed | +| ---------------------------------------------------------------------- | -----: | ----: | +| Completed small payload objects alive after timed-out close | 32 | 0 | +| Cached records after close (32 results + create + queued cancellation) | 34 | 0 | +| Canceled queued input still reachable | yes | no | +| Running native handlers after close | 1 | 1 | +| Page/executor and route/session custody retained | yes | yes | +| Close repeated before native settlement | false | false | +| whenClosed pending before native settlement | yes | yes | +| Payload objects alive after explicit native settlement | 0 | 0 | +| First late cancellation cached while sibling remains pending | 1 | 0 | + +Open request replay preserves the exact same Promise and runs once. Closed duplicates fail with dispatcher_closed. Normal native resolution and rejection both complete the existing settlement path. Executor close and route/session release happen only after native settlement in both variants. + +The initial candidate compatibility run passed 78 tests in 5 files, including its three lifecycle cases and existing dispatcher, page executor, paired-runtime composition, and paired-runtime host tests. The permanent retention suite adds two object-lifetime regressions: releasing completed results while native navigation stays pending, and releasing one late closed record while a sibling handler remains active. Final validation passed 77 tests across 5 production suites, Node typechecking, and all five explicit quality scans over product and artifact code. Reversing the patch produces the two expected lifetime failures while all 16 existing dispatcher tests pass. Commands and outcomes are recorded in `validation.json`. + +## Fix scope + +`fix.patch` changes only: + +- `src/main/browser/browser-client-host-command-dispatcher.ts`: release each page's already-settled cache during close, after cancellation; discard newly settled records instead of caching them when closed. +- `src/main/browser/browser-client-host-command-result-cache.ts`: accept an optional `retain` flag in `record`, defaulting to the existing caching behavior. When false, existing identity-checked eviction drops the settled record before any cache admission. + +Active/cancelling records, pages, native promises, abort behavior, join timing, FIFO, generation/authority checks, and the closed-settlement promise remain owned exactly as before. No row/byte cap changes. The host and executor continue waiting for their existing settlement owners. + +## retirePage is separate + +`selectCommandPage` rejects retiring and retired generations before `findExistingCommand` (`browser-client-host-command-page.ts:34–43`). Thus retired duplicate replay is already unavailable, even though the cache remains until forget/replacement. The control confirms that behavior and verifies explicit forget releases the cache and preserves the stale-generation floor. This fix leaves that existing retirement policy unchanged; freeing results on page retirement is a separate possible follow-up, especially while executor cleanup is still pending. Do not assume a live duplicate replay contract where the actual admission path rejects first. + +## Reproduction and source fences + +```sh +ORCA_BACKGROUND_LAUNCH=1 ORCA_BROWSER_CACHE_VARIANT=before pnpm exec vitest run --config docs/audits/browser-closed-result-retention/vitest.config.mjs docs/audits/browser-closed-result-retention/scenario.test.mjs +ORCA_BACKGROUND_LAUNCH=1 ORCA_BROWSER_CACHE_VARIANT=fixed pnpm exec vitest run --config docs/audits/browser-closed-result-retention/vitest.config.mjs docs/audits/browser-closed-result-retention/scenario.test.mjs +``` + +For Electron run the installed binary with `ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1`, passing node_modules/vitest/vitest.mjs and the same arguments. Reports are separate per runtime/variant; set `ORCA_BROWSER_CACHE_OUTPUT` to another file path to preserve captured reports. `sources.cjs` reverses `fix.patch` in memory and checks exact baseline/fixed hashes plus 21 caller/dependency hashes. The config loads those sources at their real production module IDs without changing checkout files. A synthetic CRLF control checks all 24 source/patch reads against canonical LF hashes. Both variants use the same controlled producer and lifecycle ports. + +`source-versions.json` records 23 canonical-LF source/caller hashes. All 23 match named main checkpoint 291b4ddd6f1c1af480169885e0fda7f9c78ff053; 21 match v1.4.198. Both fixed source baselines match both named versions. The proof executes current source/dependencies, not a historical application binary. + +## Permanent regression validation + +```sh +ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-client-host-command-retention.test.ts src/main/browser/browser-client-host-command-dispatcher.test.ts src/main/browser/browser-client-page-command-executor.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts +ORCA_BACKGROUND_LAUNCH=1 ORCA_BROWSER_CACHE_VARIANT=before pnpm exec vitest run --config docs/audits/browser-closed-result-retention/vitest.config.mjs src/main/browser/browser-client-host-command-retention.test.ts src/main/browser/browser-client-host-command-dispatcher.test.ts +ORCA_BACKGROUND_LAUNCH=1 pnpm tc:node +``` + +The baseline regression command intentionally fails the two new lifetime assertions. The source and object counts prove a code mechanism, not incident-specific browser use, native stall duration, aggregate app RSS, or attribution to #19831. diff --git a/docs/audits/browser-closed-result-retention/before-electron-results.json b/docs/audits/browser-closed-result-retention/before-electron-results.json new file mode 100644 index 00000000000..41e67e61037 --- /dev/null +++ b/docs/audits/browser-closed-result-retention/before-electron-results.json @@ -0,0 +1,138 @@ +{ + "sources": { + "src/main/browser/browser-client-host-command-dispatcher.ts": { + "before": "f8c610a54f7d16c59043250dde5ef399f70e1a974c9492d09d403138e46c1da3", + "after": "0e4074a4cb7379de58d75dd93e3f74090dea18a944bd4f038730cf19f6e68a98" + }, + "src/main/browser/browser-client-host-command-result-cache.ts": { + "before": "9ec3dad78c83f864f74957cad766cbcf41863e92d81d7e7a471702b0f9ab2f34", + "after": "f06bd1f31be9ffcbb750f242bacd0c2f16222cde7d011010acbd46ceb70ea83f" + }, + "src/main/browser/browser-client-host-command-state.ts": { + "before": "a5f6b7a3737f86b2953fc43fa0e3ed5faaa141814d5f94fd192da1bb510f355f", + "after": "a5f6b7a3737f86b2953fc43fa0e3ed5faaa141814d5f94fd192da1bb510f355f" + }, + "src/main/browser/browser-client-host-command-page.ts": { + "before": "f5ca7f90511ee0290d2a1d0b3c5c39d3c9841ebcf90a5564bce83fbc9aca06bb", + "after": "f5ca7f90511ee0290d2a1d0b3c5c39d3c9841ebcf90a5564bce83fbc9aca06bb" + }, + "src/main/browser/browser-client-host-command-join.ts": { + "before": "5bf1a38231100cc416176ef2052bdff7b3fca91b6f6dbefcd222b92be59cbb2f", + "after": "5bf1a38231100cc416176ef2052bdff7b3fca91b6f6dbefcd222b92be59cbb2f" + }, + "src/main/browser/browser-client-page-command-executor.ts": { + "before": "d249de82f89365371fa4f3b457e0da552910819555a5a0bced0c95010ccacb01", + "after": "d249de82f89365371fa4f3b457e0da552910819555a5a0bced0c95010ccacb01" + }, + "src/main/browser/browser-client-page-command-execution.ts": { + "before": "82b7c87d17b34c267ebfdf3d6ee25058cbd05dbc163ac24c43aefed00f0e3c6e", + "after": "82b7c87d17b34c267ebfdf3d6ee25058cbd05dbc163ac24c43aefed00f0e3c6e" + }, + "src/main/browser/browser-client-page-command-executor-test-harness.ts": { + "before": "b43ec764e4d094070fab51381ceb24487999dca97b4a38131be08fab0c5cbe24", + "after": "b43ec764e4d094070fab51381ceb24487999dca97b4a38131be08fab0c5cbe24" + }, + "src/main/browser/browser-client-page-automation-runtime.ts": { + "before": "7b1c0c527a87b8c22ea8290a2e1efc7ca659f2b4a5e2bd7c740fb5c22a2a6319", + "after": "7b1c0c527a87b8c22ea8290a2e1efc7ca659f2b4a5e2bd7c740fb5c22a2a6319" + }, + "src/main/browser/browser-route-guest-lifecycle.ts": { + "before": "52adbea664bcfc5d774f129d442f4fc36d4a1e1280a073053fc09ae026ca83cb", + "after": "52adbea664bcfc5d774f129d442f4fc36d4a1e1280a073053fc09ae026ca83cb" + }, + "src/main/browser/browser-route-webcontents-registry.ts": { + "before": "132c6740539aaeb9b1ceb3c108d2aebfb9e8e5c4ce3ff7cd1191e5b45dff8cad", + "after": "132c6740539aaeb9b1ceb3c108d2aebfb9e8e5c4ce3ff7cd1191e5b45dff8cad" + }, + "src/main/browser/paired-runtime-browser-client-host.ts": { + "before": "797c0130e30c5f40551f5d392326fff1ac8752e262653b3f855dfd7df9fa0038", + "after": "797c0130e30c5f40551f5d392326fff1ac8752e262653b3f855dfd7df9fa0038" + }, + "src/main/browser/paired-runtime-browser-client-host-composition.ts": { + "before": "f8b55a24035e929664f3c5f61ee8a81ee560250379dcf64b39d35e1b60628b74", + "after": "f8b55a24035e929664f3c5f61ee8a81ee560250379dcf64b39d35e1b60628b74" + }, + "src/main/browser/paired-runtime-browser-client-host-teardown.ts": { + "before": "4d9eb53c81ddfa3cb8181735b08c2b90a380bebab1f173ae5d8da9f719671f59", + "after": "4d9eb53c81ddfa3cb8181735b08c2b90a380bebab1f173ae5d8da9f719671f59" + }, + "src/main/browser/paired-runtime-browser-client-host-runtime.ts": { + "before": "2d0650d0a5ebe88f1185ab5676762ed93188e7c28a69508137ed7cfa36e3832c", + "after": "2d0650d0a5ebe88f1185ab5676762ed93188e7c28a69508137ed7cfa36e3832c" + }, + "src/main/runtime/rpc/methods/browser-core.ts": { + "before": "fdc2afeece6689644fbd607a59e484ef9d2ac6db7404e3378f2db10e16984f6d", + "after": "fdc2afeece6689644fbd607a59e484ef9d2ac6db7404e3378f2db10e16984f6d" + }, + "src/main/runtime/runtime-browser-commands-active-screencasts-by-page-id.ts": { + "before": "0af16d6fe883f6af79754e064b77a1b9ab8e80953657434eacc88cc1c523e038", + "after": "0af16d6fe883f6af79754e064b77a1b9ab8e80953657434eacc88cc1c523e038" + }, + "src/main/browser/agent-browser-bridge-types.ts": { + "before": "3ea69d15aef862807108835d1cbd536275a105a96bdc86f3be95dfbed55ab45f", + "after": "3ea69d15aef862807108835d1cbd536275a105a96bdc86f3be95dfbed55ab45f" + }, + "src/main/browser/agent-browser-bridge-raw-process.ts": { + "before": "d0e9929c4a4571d8752efa5e4063500efa75fefd228eb6cb1ea6092748befb0c", + "after": "d0e9929c4a4571d8752efa5e4063500efa75fefd228eb6cb1ea6092748befb0c" + }, + "src/main/browser/agent-browser-bridge-core-commands.ts": { + "before": "d587a7e7c3bd368c9003802607edd20def98e64442cd6d5914779c633feebd03", + "after": "d587a7e7c3bd368c9003802607edd20def98e64442cd6d5914779c633feebd03" + }, + "src/main/startup/main-process-ready-runtime.ts": { + "before": "5bb3615e9dc430e0a9ab61cb3d70e57ecbfa4c5ea82d5414fc82d93e81e9819b", + "after": "5bb3615e9dc430e0a9ab61cb3d70e57ecbfa4c5ea82d5414fc82d93e81e9819b" + }, + "src/shared/browser-client-host-protocol.ts": { + "before": "712fe3eaed30c2de322466f316c966e526dcb60cf387cc0f6ba48012c70a7956", + "after": "712fe3eaed30c2de322466f316c966e526dcb60cf387cc0f6ba48012c70a7956" + }, + "src/shared/browser-client-automation-protocol.ts": { + "before": "f93331c39fd0f8e08518d7a11a461bf5108c1a65be8f213ab2e2eff2ab977e58", + "after": "f93331c39fd0f8e08518d7a11a461bf5108c1a65be8f213ab2e2eff2ab977e58" + } + }, + "runtime": { + "node": "24.21.0", + "electron": "43.7.0", + "v8": "15.0.245.31-electron.0" + }, + "variant": "before", + "cases": [ + { + "kind": "native-navigation-close", + "completedPayloads": 32, + "heldNativePorts": 1, + "joinTimeoutOverrideMs": 15, + "retainedPayloadsAfterClose": 32, + "cachedResultsAfterClose": 34, + "cancelledQueuedInputRetained": true, + "signalAborted": true, + "executorCustodyPreserved": true, + "routeLeasePreserved": true, + "closedDuplicateRejected": true, + "secondCloseSettled": false, + "retainedAfterNativeSettlement": 0, + "executorClosedAfterNativeSettlement": true + }, + { + "kind": "late-sibling-settlement", + "oneHandlerStillOwned": true, + "cachedAfterFirstSettlement": 1, + "closedSettlementStillPending": true + }, + { + "kind": "open-replay-and-retire-contract", + "openPromiseIdentityPreserved": true, + "retiredDuplicateRejected": true, + "retireCachePolicyUnchanged": true, + "explicitForgetReleasedCache": true + }, + { + "kind": "synthetic-crlf-source-control", + "canonicalHashesMatch": true, + "reads": 24 + } + ] +} diff --git a/docs/audits/browser-closed-result-retention/before-node-results.json b/docs/audits/browser-closed-result-retention/before-node-results.json new file mode 100644 index 00000000000..69475f07d70 --- /dev/null +++ b/docs/audits/browser-closed-result-retention/before-node-results.json @@ -0,0 +1,138 @@ +{ + "sources": { + "src/main/browser/browser-client-host-command-dispatcher.ts": { + "before": "f8c610a54f7d16c59043250dde5ef399f70e1a974c9492d09d403138e46c1da3", + "after": "0e4074a4cb7379de58d75dd93e3f74090dea18a944bd4f038730cf19f6e68a98" + }, + "src/main/browser/browser-client-host-command-result-cache.ts": { + "before": "9ec3dad78c83f864f74957cad766cbcf41863e92d81d7e7a471702b0f9ab2f34", + "after": "f06bd1f31be9ffcbb750f242bacd0c2f16222cde7d011010acbd46ceb70ea83f" + }, + "src/main/browser/browser-client-host-command-state.ts": { + "before": "a5f6b7a3737f86b2953fc43fa0e3ed5faaa141814d5f94fd192da1bb510f355f", + "after": "a5f6b7a3737f86b2953fc43fa0e3ed5faaa141814d5f94fd192da1bb510f355f" + }, + "src/main/browser/browser-client-host-command-page.ts": { + "before": "f5ca7f90511ee0290d2a1d0b3c5c39d3c9841ebcf90a5564bce83fbc9aca06bb", + "after": "f5ca7f90511ee0290d2a1d0b3c5c39d3c9841ebcf90a5564bce83fbc9aca06bb" + }, + "src/main/browser/browser-client-host-command-join.ts": { + "before": "5bf1a38231100cc416176ef2052bdff7b3fca91b6f6dbefcd222b92be59cbb2f", + "after": "5bf1a38231100cc416176ef2052bdff7b3fca91b6f6dbefcd222b92be59cbb2f" + }, + "src/main/browser/browser-client-page-command-executor.ts": { + "before": "d249de82f89365371fa4f3b457e0da552910819555a5a0bced0c95010ccacb01", + "after": "d249de82f89365371fa4f3b457e0da552910819555a5a0bced0c95010ccacb01" + }, + "src/main/browser/browser-client-page-command-execution.ts": { + "before": "82b7c87d17b34c267ebfdf3d6ee25058cbd05dbc163ac24c43aefed00f0e3c6e", + "after": "82b7c87d17b34c267ebfdf3d6ee25058cbd05dbc163ac24c43aefed00f0e3c6e" + }, + "src/main/browser/browser-client-page-command-executor-test-harness.ts": { + "before": "b43ec764e4d094070fab51381ceb24487999dca97b4a38131be08fab0c5cbe24", + "after": "b43ec764e4d094070fab51381ceb24487999dca97b4a38131be08fab0c5cbe24" + }, + "src/main/browser/browser-client-page-automation-runtime.ts": { + "before": "7b1c0c527a87b8c22ea8290a2e1efc7ca659f2b4a5e2bd7c740fb5c22a2a6319", + "after": "7b1c0c527a87b8c22ea8290a2e1efc7ca659f2b4a5e2bd7c740fb5c22a2a6319" + }, + "src/main/browser/browser-route-guest-lifecycle.ts": { + "before": "52adbea664bcfc5d774f129d442f4fc36d4a1e1280a073053fc09ae026ca83cb", + "after": "52adbea664bcfc5d774f129d442f4fc36d4a1e1280a073053fc09ae026ca83cb" + }, + "src/main/browser/browser-route-webcontents-registry.ts": { + "before": "132c6740539aaeb9b1ceb3c108d2aebfb9e8e5c4ce3ff7cd1191e5b45dff8cad", + "after": "132c6740539aaeb9b1ceb3c108d2aebfb9e8e5c4ce3ff7cd1191e5b45dff8cad" + }, + "src/main/browser/paired-runtime-browser-client-host.ts": { + "before": "797c0130e30c5f40551f5d392326fff1ac8752e262653b3f855dfd7df9fa0038", + "after": "797c0130e30c5f40551f5d392326fff1ac8752e262653b3f855dfd7df9fa0038" + }, + "src/main/browser/paired-runtime-browser-client-host-composition.ts": { + "before": "f8b55a24035e929664f3c5f61ee8a81ee560250379dcf64b39d35e1b60628b74", + "after": "f8b55a24035e929664f3c5f61ee8a81ee560250379dcf64b39d35e1b60628b74" + }, + "src/main/browser/paired-runtime-browser-client-host-teardown.ts": { + "before": "4d9eb53c81ddfa3cb8181735b08c2b90a380bebab1f173ae5d8da9f719671f59", + "after": "4d9eb53c81ddfa3cb8181735b08c2b90a380bebab1f173ae5d8da9f719671f59" + }, + "src/main/browser/paired-runtime-browser-client-host-runtime.ts": { + "before": "2d0650d0a5ebe88f1185ab5676762ed93188e7c28a69508137ed7cfa36e3832c", + "after": "2d0650d0a5ebe88f1185ab5676762ed93188e7c28a69508137ed7cfa36e3832c" + }, + "src/main/runtime/rpc/methods/browser-core.ts": { + "before": "fdc2afeece6689644fbd607a59e484ef9d2ac6db7404e3378f2db10e16984f6d", + "after": "fdc2afeece6689644fbd607a59e484ef9d2ac6db7404e3378f2db10e16984f6d" + }, + "src/main/runtime/runtime-browser-commands-active-screencasts-by-page-id.ts": { + "before": "0af16d6fe883f6af79754e064b77a1b9ab8e80953657434eacc88cc1c523e038", + "after": "0af16d6fe883f6af79754e064b77a1b9ab8e80953657434eacc88cc1c523e038" + }, + "src/main/browser/agent-browser-bridge-types.ts": { + "before": "3ea69d15aef862807108835d1cbd536275a105a96bdc86f3be95dfbed55ab45f", + "after": "3ea69d15aef862807108835d1cbd536275a105a96bdc86f3be95dfbed55ab45f" + }, + "src/main/browser/agent-browser-bridge-raw-process.ts": { + "before": "d0e9929c4a4571d8752efa5e4063500efa75fefd228eb6cb1ea6092748befb0c", + "after": "d0e9929c4a4571d8752efa5e4063500efa75fefd228eb6cb1ea6092748befb0c" + }, + "src/main/browser/agent-browser-bridge-core-commands.ts": { + "before": "d587a7e7c3bd368c9003802607edd20def98e64442cd6d5914779c633feebd03", + "after": "d587a7e7c3bd368c9003802607edd20def98e64442cd6d5914779c633feebd03" + }, + "src/main/startup/main-process-ready-runtime.ts": { + "before": "5bb3615e9dc430e0a9ab61cb3d70e57ecbfa4c5ea82d5414fc82d93e81e9819b", + "after": "5bb3615e9dc430e0a9ab61cb3d70e57ecbfa4c5ea82d5414fc82d93e81e9819b" + }, + "src/shared/browser-client-host-protocol.ts": { + "before": "712fe3eaed30c2de322466f316c966e526dcb60cf387cc0f6ba48012c70a7956", + "after": "712fe3eaed30c2de322466f316c966e526dcb60cf387cc0f6ba48012c70a7956" + }, + "src/shared/browser-client-automation-protocol.ts": { + "before": "f93331c39fd0f8e08518d7a11a461bf5108c1a65be8f213ab2e2eff2ab977e58", + "after": "f93331c39fd0f8e08518d7a11a461bf5108c1a65be8f213ab2e2eff2ab977e58" + } + }, + "runtime": { + "node": "26.6.0", + "electron": null, + "v8": "14.6.202.34-node.26" + }, + "variant": "before", + "cases": [ + { + "kind": "native-navigation-close", + "completedPayloads": 32, + "heldNativePorts": 1, + "joinTimeoutOverrideMs": 15, + "retainedPayloadsAfterClose": 32, + "cachedResultsAfterClose": 34, + "cancelledQueuedInputRetained": true, + "signalAborted": true, + "executorCustodyPreserved": true, + "routeLeasePreserved": true, + "closedDuplicateRejected": true, + "secondCloseSettled": false, + "retainedAfterNativeSettlement": 0, + "executorClosedAfterNativeSettlement": true + }, + { + "kind": "late-sibling-settlement", + "oneHandlerStillOwned": true, + "cachedAfterFirstSettlement": 1, + "closedSettlementStillPending": true + }, + { + "kind": "open-replay-and-retire-contract", + "openPromiseIdentityPreserved": true, + "retiredDuplicateRejected": true, + "retireCachePolicyUnchanged": true, + "explicitForgetReleasedCache": true + }, + { + "kind": "synthetic-crlf-source-control", + "canonicalHashesMatch": true, + "reads": 24 + } + ] +} diff --git a/docs/audits/browser-closed-result-retention/fix.patch b/docs/audits/browser-closed-result-retention/fix.patch new file mode 100644 index 00000000000..1213361b4e3 --- /dev/null +++ b/docs/audits/browser-closed-result-retention/fix.patch @@ -0,0 +1,16 @@ +--- a/src/main/browser/browser-client-host-command-dispatcher.ts ++++ b/src/main/browser/browser-client-host-command-dispatcher.ts +@@ -165,0 +166 @@ ++ this.resultCache.releasePage(page) +@@ -300 +301 @@ +- this.resultCache.record(page, record) ++ this.resultCache.record(page, record, !this.closed) +--- a/src/main/browser/browser-client-host-command-result-cache.ts ++++ b/src/main/browser/browser-client-host-command-result-cache.ts +@@ -11 +11,5 @@ +- record(page: PageState, record: CommandRecord): void { ++ record(page: PageState, record: CommandRecord, retain = true): void { ++ if (!retain) { ++ this.evict(page, record.event.commandSequence, record) ++ return ++ } diff --git a/docs/audits/browser-closed-result-retention/fixed-electron-results.json b/docs/audits/browser-closed-result-retention/fixed-electron-results.json new file mode 100644 index 00000000000..7bcb23d2fcd --- /dev/null +++ b/docs/audits/browser-closed-result-retention/fixed-electron-results.json @@ -0,0 +1,138 @@ +{ + "sources": { + "src/main/browser/browser-client-host-command-dispatcher.ts": { + "before": "f8c610a54f7d16c59043250dde5ef399f70e1a974c9492d09d403138e46c1da3", + "after": "0e4074a4cb7379de58d75dd93e3f74090dea18a944bd4f038730cf19f6e68a98" + }, + "src/main/browser/browser-client-host-command-result-cache.ts": { + "before": "9ec3dad78c83f864f74957cad766cbcf41863e92d81d7e7a471702b0f9ab2f34", + "after": "f06bd1f31be9ffcbb750f242bacd0c2f16222cde7d011010acbd46ceb70ea83f" + }, + "src/main/browser/browser-client-host-command-state.ts": { + "before": "a5f6b7a3737f86b2953fc43fa0e3ed5faaa141814d5f94fd192da1bb510f355f", + "after": "a5f6b7a3737f86b2953fc43fa0e3ed5faaa141814d5f94fd192da1bb510f355f" + }, + "src/main/browser/browser-client-host-command-page.ts": { + "before": "f5ca7f90511ee0290d2a1d0b3c5c39d3c9841ebcf90a5564bce83fbc9aca06bb", + "after": "f5ca7f90511ee0290d2a1d0b3c5c39d3c9841ebcf90a5564bce83fbc9aca06bb" + }, + "src/main/browser/browser-client-host-command-join.ts": { + "before": "5bf1a38231100cc416176ef2052bdff7b3fca91b6f6dbefcd222b92be59cbb2f", + "after": "5bf1a38231100cc416176ef2052bdff7b3fca91b6f6dbefcd222b92be59cbb2f" + }, + "src/main/browser/browser-client-page-command-executor.ts": { + "before": "d249de82f89365371fa4f3b457e0da552910819555a5a0bced0c95010ccacb01", + "after": "d249de82f89365371fa4f3b457e0da552910819555a5a0bced0c95010ccacb01" + }, + "src/main/browser/browser-client-page-command-execution.ts": { + "before": "82b7c87d17b34c267ebfdf3d6ee25058cbd05dbc163ac24c43aefed00f0e3c6e", + "after": "82b7c87d17b34c267ebfdf3d6ee25058cbd05dbc163ac24c43aefed00f0e3c6e" + }, + "src/main/browser/browser-client-page-command-executor-test-harness.ts": { + "before": "b43ec764e4d094070fab51381ceb24487999dca97b4a38131be08fab0c5cbe24", + "after": "b43ec764e4d094070fab51381ceb24487999dca97b4a38131be08fab0c5cbe24" + }, + "src/main/browser/browser-client-page-automation-runtime.ts": { + "before": "7b1c0c527a87b8c22ea8290a2e1efc7ca659f2b4a5e2bd7c740fb5c22a2a6319", + "after": "7b1c0c527a87b8c22ea8290a2e1efc7ca659f2b4a5e2bd7c740fb5c22a2a6319" + }, + "src/main/browser/browser-route-guest-lifecycle.ts": { + "before": "52adbea664bcfc5d774f129d442f4fc36d4a1e1280a073053fc09ae026ca83cb", + "after": "52adbea664bcfc5d774f129d442f4fc36d4a1e1280a073053fc09ae026ca83cb" + }, + "src/main/browser/browser-route-webcontents-registry.ts": { + "before": "132c6740539aaeb9b1ceb3c108d2aebfb9e8e5c4ce3ff7cd1191e5b45dff8cad", + "after": "132c6740539aaeb9b1ceb3c108d2aebfb9e8e5c4ce3ff7cd1191e5b45dff8cad" + }, + "src/main/browser/paired-runtime-browser-client-host.ts": { + "before": "797c0130e30c5f40551f5d392326fff1ac8752e262653b3f855dfd7df9fa0038", + "after": "797c0130e30c5f40551f5d392326fff1ac8752e262653b3f855dfd7df9fa0038" + }, + "src/main/browser/paired-runtime-browser-client-host-composition.ts": { + "before": "f8b55a24035e929664f3c5f61ee8a81ee560250379dcf64b39d35e1b60628b74", + "after": "f8b55a24035e929664f3c5f61ee8a81ee560250379dcf64b39d35e1b60628b74" + }, + "src/main/browser/paired-runtime-browser-client-host-teardown.ts": { + "before": "4d9eb53c81ddfa3cb8181735b08c2b90a380bebab1f173ae5d8da9f719671f59", + "after": "4d9eb53c81ddfa3cb8181735b08c2b90a380bebab1f173ae5d8da9f719671f59" + }, + "src/main/browser/paired-runtime-browser-client-host-runtime.ts": { + "before": "2d0650d0a5ebe88f1185ab5676762ed93188e7c28a69508137ed7cfa36e3832c", + "after": "2d0650d0a5ebe88f1185ab5676762ed93188e7c28a69508137ed7cfa36e3832c" + }, + "src/main/runtime/rpc/methods/browser-core.ts": { + "before": "fdc2afeece6689644fbd607a59e484ef9d2ac6db7404e3378f2db10e16984f6d", + "after": "fdc2afeece6689644fbd607a59e484ef9d2ac6db7404e3378f2db10e16984f6d" + }, + "src/main/runtime/runtime-browser-commands-active-screencasts-by-page-id.ts": { + "before": "0af16d6fe883f6af79754e064b77a1b9ab8e80953657434eacc88cc1c523e038", + "after": "0af16d6fe883f6af79754e064b77a1b9ab8e80953657434eacc88cc1c523e038" + }, + "src/main/browser/agent-browser-bridge-types.ts": { + "before": "3ea69d15aef862807108835d1cbd536275a105a96bdc86f3be95dfbed55ab45f", + "after": "3ea69d15aef862807108835d1cbd536275a105a96bdc86f3be95dfbed55ab45f" + }, + "src/main/browser/agent-browser-bridge-raw-process.ts": { + "before": "d0e9929c4a4571d8752efa5e4063500efa75fefd228eb6cb1ea6092748befb0c", + "after": "d0e9929c4a4571d8752efa5e4063500efa75fefd228eb6cb1ea6092748befb0c" + }, + "src/main/browser/agent-browser-bridge-core-commands.ts": { + "before": "d587a7e7c3bd368c9003802607edd20def98e64442cd6d5914779c633feebd03", + "after": "d587a7e7c3bd368c9003802607edd20def98e64442cd6d5914779c633feebd03" + }, + "src/main/startup/main-process-ready-runtime.ts": { + "before": "5bb3615e9dc430e0a9ab61cb3d70e57ecbfa4c5ea82d5414fc82d93e81e9819b", + "after": "5bb3615e9dc430e0a9ab61cb3d70e57ecbfa4c5ea82d5414fc82d93e81e9819b" + }, + "src/shared/browser-client-host-protocol.ts": { + "before": "712fe3eaed30c2de322466f316c966e526dcb60cf387cc0f6ba48012c70a7956", + "after": "712fe3eaed30c2de322466f316c966e526dcb60cf387cc0f6ba48012c70a7956" + }, + "src/shared/browser-client-automation-protocol.ts": { + "before": "f93331c39fd0f8e08518d7a11a461bf5108c1a65be8f213ab2e2eff2ab977e58", + "after": "f93331c39fd0f8e08518d7a11a461bf5108c1a65be8f213ab2e2eff2ab977e58" + } + }, + "runtime": { + "node": "24.21.0", + "electron": "43.7.0", + "v8": "15.0.245.31-electron.0" + }, + "variant": "fixed", + "cases": [ + { + "kind": "native-navigation-close", + "completedPayloads": 32, + "heldNativePorts": 1, + "joinTimeoutOverrideMs": 15, + "retainedPayloadsAfterClose": 0, + "cachedResultsAfterClose": 0, + "cancelledQueuedInputRetained": false, + "signalAborted": true, + "executorCustodyPreserved": true, + "routeLeasePreserved": true, + "closedDuplicateRejected": true, + "secondCloseSettled": false, + "retainedAfterNativeSettlement": 0, + "executorClosedAfterNativeSettlement": true + }, + { + "kind": "late-sibling-settlement", + "oneHandlerStillOwned": true, + "cachedAfterFirstSettlement": 0, + "closedSettlementStillPending": true + }, + { + "kind": "open-replay-and-retire-contract", + "openPromiseIdentityPreserved": true, + "retiredDuplicateRejected": true, + "retireCachePolicyUnchanged": true, + "explicitForgetReleasedCache": true + }, + { + "kind": "synthetic-crlf-source-control", + "canonicalHashesMatch": true, + "reads": 24 + } + ] +} diff --git a/docs/audits/browser-closed-result-retention/fixed-node-results.json b/docs/audits/browser-closed-result-retention/fixed-node-results.json new file mode 100644 index 00000000000..f30dea10cf7 --- /dev/null +++ b/docs/audits/browser-closed-result-retention/fixed-node-results.json @@ -0,0 +1,138 @@ +{ + "sources": { + "src/main/browser/browser-client-host-command-dispatcher.ts": { + "before": "f8c610a54f7d16c59043250dde5ef399f70e1a974c9492d09d403138e46c1da3", + "after": "0e4074a4cb7379de58d75dd93e3f74090dea18a944bd4f038730cf19f6e68a98" + }, + "src/main/browser/browser-client-host-command-result-cache.ts": { + "before": "9ec3dad78c83f864f74957cad766cbcf41863e92d81d7e7a471702b0f9ab2f34", + "after": "f06bd1f31be9ffcbb750f242bacd0c2f16222cde7d011010acbd46ceb70ea83f" + }, + "src/main/browser/browser-client-host-command-state.ts": { + "before": "a5f6b7a3737f86b2953fc43fa0e3ed5faaa141814d5f94fd192da1bb510f355f", + "after": "a5f6b7a3737f86b2953fc43fa0e3ed5faaa141814d5f94fd192da1bb510f355f" + }, + "src/main/browser/browser-client-host-command-page.ts": { + "before": "f5ca7f90511ee0290d2a1d0b3c5c39d3c9841ebcf90a5564bce83fbc9aca06bb", + "after": "f5ca7f90511ee0290d2a1d0b3c5c39d3c9841ebcf90a5564bce83fbc9aca06bb" + }, + "src/main/browser/browser-client-host-command-join.ts": { + "before": "5bf1a38231100cc416176ef2052bdff7b3fca91b6f6dbefcd222b92be59cbb2f", + "after": "5bf1a38231100cc416176ef2052bdff7b3fca91b6f6dbefcd222b92be59cbb2f" + }, + "src/main/browser/browser-client-page-command-executor.ts": { + "before": "d249de82f89365371fa4f3b457e0da552910819555a5a0bced0c95010ccacb01", + "after": "d249de82f89365371fa4f3b457e0da552910819555a5a0bced0c95010ccacb01" + }, + "src/main/browser/browser-client-page-command-execution.ts": { + "before": "82b7c87d17b34c267ebfdf3d6ee25058cbd05dbc163ac24c43aefed00f0e3c6e", + "after": "82b7c87d17b34c267ebfdf3d6ee25058cbd05dbc163ac24c43aefed00f0e3c6e" + }, + "src/main/browser/browser-client-page-command-executor-test-harness.ts": { + "before": "b43ec764e4d094070fab51381ceb24487999dca97b4a38131be08fab0c5cbe24", + "after": "b43ec764e4d094070fab51381ceb24487999dca97b4a38131be08fab0c5cbe24" + }, + "src/main/browser/browser-client-page-automation-runtime.ts": { + "before": "7b1c0c527a87b8c22ea8290a2e1efc7ca659f2b4a5e2bd7c740fb5c22a2a6319", + "after": "7b1c0c527a87b8c22ea8290a2e1efc7ca659f2b4a5e2bd7c740fb5c22a2a6319" + }, + "src/main/browser/browser-route-guest-lifecycle.ts": { + "before": "52adbea664bcfc5d774f129d442f4fc36d4a1e1280a073053fc09ae026ca83cb", + "after": "52adbea664bcfc5d774f129d442f4fc36d4a1e1280a073053fc09ae026ca83cb" + }, + "src/main/browser/browser-route-webcontents-registry.ts": { + "before": "132c6740539aaeb9b1ceb3c108d2aebfb9e8e5c4ce3ff7cd1191e5b45dff8cad", + "after": "132c6740539aaeb9b1ceb3c108d2aebfb9e8e5c4ce3ff7cd1191e5b45dff8cad" + }, + "src/main/browser/paired-runtime-browser-client-host.ts": { + "before": "797c0130e30c5f40551f5d392326fff1ac8752e262653b3f855dfd7df9fa0038", + "after": "797c0130e30c5f40551f5d392326fff1ac8752e262653b3f855dfd7df9fa0038" + }, + "src/main/browser/paired-runtime-browser-client-host-composition.ts": { + "before": "f8b55a24035e929664f3c5f61ee8a81ee560250379dcf64b39d35e1b60628b74", + "after": "f8b55a24035e929664f3c5f61ee8a81ee560250379dcf64b39d35e1b60628b74" + }, + "src/main/browser/paired-runtime-browser-client-host-teardown.ts": { + "before": "4d9eb53c81ddfa3cb8181735b08c2b90a380bebab1f173ae5d8da9f719671f59", + "after": "4d9eb53c81ddfa3cb8181735b08c2b90a380bebab1f173ae5d8da9f719671f59" + }, + "src/main/browser/paired-runtime-browser-client-host-runtime.ts": { + "before": "2d0650d0a5ebe88f1185ab5676762ed93188e7c28a69508137ed7cfa36e3832c", + "after": "2d0650d0a5ebe88f1185ab5676762ed93188e7c28a69508137ed7cfa36e3832c" + }, + "src/main/runtime/rpc/methods/browser-core.ts": { + "before": "fdc2afeece6689644fbd607a59e484ef9d2ac6db7404e3378f2db10e16984f6d", + "after": "fdc2afeece6689644fbd607a59e484ef9d2ac6db7404e3378f2db10e16984f6d" + }, + "src/main/runtime/runtime-browser-commands-active-screencasts-by-page-id.ts": { + "before": "0af16d6fe883f6af79754e064b77a1b9ab8e80953657434eacc88cc1c523e038", + "after": "0af16d6fe883f6af79754e064b77a1b9ab8e80953657434eacc88cc1c523e038" + }, + "src/main/browser/agent-browser-bridge-types.ts": { + "before": "3ea69d15aef862807108835d1cbd536275a105a96bdc86f3be95dfbed55ab45f", + "after": "3ea69d15aef862807108835d1cbd536275a105a96bdc86f3be95dfbed55ab45f" + }, + "src/main/browser/agent-browser-bridge-raw-process.ts": { + "before": "d0e9929c4a4571d8752efa5e4063500efa75fefd228eb6cb1ea6092748befb0c", + "after": "d0e9929c4a4571d8752efa5e4063500efa75fefd228eb6cb1ea6092748befb0c" + }, + "src/main/browser/agent-browser-bridge-core-commands.ts": { + "before": "d587a7e7c3bd368c9003802607edd20def98e64442cd6d5914779c633feebd03", + "after": "d587a7e7c3bd368c9003802607edd20def98e64442cd6d5914779c633feebd03" + }, + "src/main/startup/main-process-ready-runtime.ts": { + "before": "5bb3615e9dc430e0a9ab61cb3d70e57ecbfa4c5ea82d5414fc82d93e81e9819b", + "after": "5bb3615e9dc430e0a9ab61cb3d70e57ecbfa4c5ea82d5414fc82d93e81e9819b" + }, + "src/shared/browser-client-host-protocol.ts": { + "before": "712fe3eaed30c2de322466f316c966e526dcb60cf387cc0f6ba48012c70a7956", + "after": "712fe3eaed30c2de322466f316c966e526dcb60cf387cc0f6ba48012c70a7956" + }, + "src/shared/browser-client-automation-protocol.ts": { + "before": "f93331c39fd0f8e08518d7a11a461bf5108c1a65be8f213ab2e2eff2ab977e58", + "after": "f93331c39fd0f8e08518d7a11a461bf5108c1a65be8f213ab2e2eff2ab977e58" + } + }, + "runtime": { + "node": "26.6.0", + "electron": null, + "v8": "14.6.202.34-node.26" + }, + "variant": "fixed", + "cases": [ + { + "kind": "native-navigation-close", + "completedPayloads": 32, + "heldNativePorts": 1, + "joinTimeoutOverrideMs": 15, + "retainedPayloadsAfterClose": 0, + "cachedResultsAfterClose": 0, + "cancelledQueuedInputRetained": false, + "signalAborted": true, + "executorCustodyPreserved": true, + "routeLeasePreserved": true, + "closedDuplicateRejected": true, + "secondCloseSettled": false, + "retainedAfterNativeSettlement": 0, + "executorClosedAfterNativeSettlement": true + }, + { + "kind": "late-sibling-settlement", + "oneHandlerStillOwned": true, + "cachedAfterFirstSettlement": 0, + "closedSettlementStillPending": true + }, + { + "kind": "open-replay-and-retire-contract", + "openPromiseIdentityPreserved": true, + "retiredDuplicateRejected": true, + "retireCachePolicyUnchanged": true, + "explicitForgetReleasedCache": true + }, + { + "kind": "synthetic-crlf-source-control", + "canonicalHashesMatch": true, + "reads": 24 + } + ] +} diff --git a/docs/audits/browser-closed-result-retention/scenario.test.mjs b/docs/audits/browser-closed-result-retention/scenario.test.mjs new file mode 100644 index 00000000000..8e1bab9406f --- /dev/null +++ b/docs/audits/browser-closed-result-retention/scenario.test.mjs @@ -0,0 +1,330 @@ +import { afterEach, expect, it, vi } from 'vitest' +import { writeFileSync, readFileSync } from 'node:fs' +import { createRequire } from 'node:module' +const { loadSources } = createRequire(import.meta.url)('./sources.cjs') +const sourceInfo = loadSources() +import { BrowserClientHostCommandDispatcher } from '../../../src/main/browser/browser-client-host-command-dispatcher' +import { + createHarness, + createCommand +} from '../../../src/main/browser/browser-client-page-command-executor-test-harness' +import { BrowserClientPageAutomationRuntime } from '../../../src/main/browser/browser-client-page-automation-runtime' +import { navigateBrowserRouteGuest } from '../../../src/main/browser/browser-route-guest-lifecycle' +import { closeBrowserClientHostComposition } from '../../../src/main/browser/paired-runtime-browser-client-host-teardown' +import { BROWSER_CORE_METHODS } from '../../../src/main/runtime/rpc/methods/browser-core' + +const fixed = process.env.ORCA_BROWSER_CACHE_VARIANT !== 'before' +const variant = fixed ? 'fixed' : 'before', + reports = [] +const authority = { + authorityRuntimeId: 'runtime-a', + authorityEpoch: 'epoch-a', + browserHostClientId: 'client-a', + browserHostGeneration: 3, + pageCommandProtocolVersion: 1 +} +function gate() { + let resolve, reject + const promise = new Promise((yes, no) => { + resolve = yes + reject = no + }) + return { promise, resolve, reject } +} +async function collect() { + for (let turn = 0; turn < 8; turn++) { + await new Promise(setImmediate) + global.gc() + } +} +function alive(refs) { + return refs.filter((ref) => ref.deref()).length +} +function command(sequence, body, page = 'page-a', generation = 7) { + return createCommand('createPage', { + browserPageId: page, + pageHostGeneration: generation, + commandSequence: sequence, + commandId: `${page}-${generation}-${sequence}`, + command: body + }) +} +function cached(dispatcher) { + return [...dispatcher.pages.values()].reduce((sum, page) => sum + page.settledSequences.length, 0) +} +function queueUnstartedPayload(dispatcher) { + const payload = { queued: 'small-command-input' } + return { + ref: new WeakRef(payload), + promise: dispatcher.dispatch( + command(35, { type: 'automation', method: 'browser.snapshot', params: { payload } }) + ) + } +} +async function appendSnapshot(dispatcher, index) { + const result = await dispatcher.dispatch( + command(index + 2, { type: 'automation', method: 'browser.snapshot', params: {} }) + ) + expect(result.status).toBe('completed') + return new WeakRef(result.value) +} +afterEach(() => { + vi.restoreAllMocks() + writeFileSync( + process.env.ORCA_BROWSER_CACHE_OUTPUT ?? + `docs/audits/browser-closed-result-retention/${variant}-${process.versions.electron ? 'electron' : 'node'}-results.json`, + `${JSON.stringify( + { + sources: sourceInfo.hashes, + runtime: { + node: process.versions.node, + electron: process.versions.electron ?? null, + v8: process.versions.v8 + }, + variant, + cases: reports + }, + null, + 2 + )}\n` + ) +}) + +it('keeps completed actual automation results behind one pending native navigation after close timeout', async () => { + const h = createHarness(), + native = gate(), + entered = gate() + let signal, + ordinal = 0, + executorClosed = false, + deferredClose + const snapshot = BROWSER_CORE_METHODS.find((method) => method.name === 'browser.snapshot') + const automation = new BrowserClientPageAutomationRuntime({ + browserManager: { + getGuestWebContentsId: () => 41, + registerGuest: () => true, + unregisterGuest() {} + }, + getAgentBrowserBridge: () => null, + executeRpc: (_method, params, contextSignal) => + snapshot.handler(params, { + signal: contextSignal, + runtime: { + browserSnapshot: async () => ({ title: `ordinary-result-${ordinal++}`, items: [1, 2, 3] }) + } + }) + }) + h.dependencies.executeAutomation = (input, contextSignal) => + automation.execute(input, contextSignal) + h.dependencies.retireAutomation = (input) => automation.retire(input) + h.dependencies.routeWebContents.navigateGuest = (claim, url) => + navigateBrowserRouteGuest( + claim.registration, + url, + { + registration: claim.registration, + navigationGranted: true, + guest: { + loadURL: () => { + entered.resolve() + return native.promise + } + } + }, + () => true + ) + const dispatcher = new BrowserClientHostCommandDispatcher({ + authority, + handler: (event, contextSignal) => { + if (event.command.type === 'navigate') { + signal = contextSignal + } + return h.executor.handle(event, contextSignal) + }, + joinTimeoutMs: 15 + }) + await dispatcher.dispatch(createCommand('createPage')) + const refs = [] + for (let index = 0; index < 32; index++) { + refs.push(await appendSnapshot(dispatcher, index)) + } + await collect() + expect(alive(refs)).toBe(32) + const pending = dispatcher.dispatch( + command(34, { type: 'navigate', url: 'https://example.invalid/held' }) + ) + await entered.promise + const queued = queueUnstartedPayload(dispatcher) + h.executor.fenceNavigation() + const closing = closeBrowserClientHostComposition({ + host: { close: () => dispatcher.close(), whenHandlersSettled: () => dispatcher.whenClosed() }, + executor: { + async close() { + executorClosed = true + await h.executor.close() + } + }, + routeSets: { async close() {} }, + error: new Error('controlled disconnect'), + deferExecutorClose: (close) => { + deferredClose = close + }, + reportCleanupError: (error) => { + throw error + } + }) + try { + expect(await closing).toBe(false) + expect(await pending).toMatchObject({ + status: 'failed', + errorCode: 'browser_host_command_cancelled' + }) + expect(await queued.promise).toMatchObject({ + status: 'failed', + errorCode: 'browser_host_command_cancelled' + }) + expect(signal.aborted).toBe(true) + expect(executorClosed).toBe(false) + expect(h.executor.hasPage('page-a', 7)).toBe(true) + expect(h.route.release).not.toHaveBeenCalled() + expect(h.routeSession.release).not.toHaveBeenCalled() + expect(() => dispatcher.dispatch(createCommand('createPage'))).toThrow('dispatcher_closed') + expect(await dispatcher.close()).toBe(false) + let settled = false + void dispatcher.whenClosed().then(() => { + settled = true + }) + await collect() + expect(settled).toBe(false) + const retained = alive(refs), + cachedResults = cached(dispatcher) + expect(retained).toBe(fixed ? 0 : 32) + expect(cachedResults).toBe(fixed ? 0 : 34) + expect(Boolean(queued.ref.deref())).toBe(!fixed) + expect(dispatcher.runningHandlers).toBe(1) + reports.push({ + kind: 'native-navigation-close', + completedPayloads: 32, + heldNativePorts: 1, + joinTimeoutOverrideMs: 15, + retainedPayloadsAfterClose: retained, + cachedResultsAfterClose: cachedResults, + cancelledQueuedInputRetained: Boolean(queued.ref.deref()), + signalAborted: true, + executorCustodyPreserved: true, + routeLeasePreserved: true, + closedDuplicateRejected: true, + secondCloseSettled: false + }) + } finally { + native.resolve() + await dispatcher.whenClosed() + await deferredClose + await h.executor.close() + } + await collect() + expect(alive(refs)).toBe(0) + expect(executorClosed).toBe(true) + expect(h.route.release).toHaveBeenCalledOnce() + expect(h.routeSession.release).toHaveBeenCalledOnce() + reports.at(-1).retainedAfterNativeSettlement = alive(refs) + reports.at(-1).executorClosedAfterNativeSettlement = true +}) + +it('does not retain late completed cancellation records while a sibling native handler remains owned', async () => { + const first = gate(), + second = gate() + const dispatcher = new BrowserClientHostCommandDispatcher({ + authority, + joinTimeoutMs: 15, + handler: (event) => (event.browserPageId === 'page-a' ? first.promise : second.promise) + }) + const firstResult = dispatcher.dispatch( + command( + 1, + { type: 'createPage', browserProfileId: 'profile-a', executionHostKey: 'execution-host-a' }, + 'page-a' + ) + ) + const secondResult = dispatcher.dispatch( + command( + 1, + { type: 'createPage', browserProfileId: 'profile-a', executionHostKey: 'execution-host-a' }, + 'page-b' + ) + ) + expect(await dispatcher.close()).toBe(false) + expect(await firstResult).toMatchObject({ errorCode: 'browser_host_command_cancelled' }) + expect(await secondResult).toMatchObject({ errorCode: 'browser_host_command_cancelled' }) + first.resolve({ status: 'completed', value: { late: 'ignored' } }) + await new Promise(setImmediate) + expect(dispatcher.runningHandlers).toBe(1) + expect(cached(dispatcher)).toBe(fixed ? 0 : 1) + expect(dispatcher.pages.get('page-a').records.size).toBe(fixed ? 0 : 1) + let settled = false + void dispatcher.whenClosed().then(() => { + settled = true + }) + await new Promise(setImmediate) + expect(settled).toBe(false) + reports.push({ + kind: 'late-sibling-settlement', + oneHandlerStillOwned: true, + cachedAfterFirstSettlement: cached(dispatcher), + closedSettlementStillPending: true + }) + second.reject(new Error('controlled native failure')) + await dispatcher.whenClosed() + expect(dispatcher.runningHandlers).toBe(0) + expect(dispatcher.pages.size).toBe(0) +}) + +it('preserves open replay and generation fencing independently of closed cache release', async () => { + let calls = 0 + const dispatcher = new BrowserClientHostCommandDispatcher({ + authority, + handler: () => { + calls++ + return { status: 'completed', value: { ordinary: true } } + } + }) + const event = command(1, { + type: 'createPage', + browserProfileId: 'profile-a', + executionHostKey: 'execution-host-a' + }) + const original = dispatcher.dispatch(event), + duplicate = dispatcher.dispatch(event) + expect(duplicate).toBe(original) + await original + expect(dispatcher.dispatch(event)).toBe(original) + expect(calls).toBe(1) + expect(await dispatcher.retirePage('page-a', 7)).toBe(true) + expect(() => dispatcher.dispatch(event)).toThrow('generation_stale') + expect(cached(dispatcher)).toBe(1) + expect(dispatcher.forgetPage('page-a', 7)).toBe(true) + expect(cached(dispatcher)).toBe(0) + expect(() => dispatcher.dispatch(event)).toThrow('generation_stale') + expect(await dispatcher.close()).toBe(true) + reports.push({ + kind: 'open-replay-and-retire-contract', + openPromiseIdentityPreserved: true, + retiredDuplicateRejected: true, + retireCachePolicyUnchanged: true, + explicitForgetReleasedCache: true + }) +}) + +it('loads identical canonical hashes from synthetic CRLF source and patch reads', () => { + let reads = 0 + const crlf = loadSources({ + readText: (filename) => { + reads++ + return readFileSync(filename, 'utf8').replace(/\r?\n/g, '\r\n') + } + }) + expect(crlf.hashes).toEqual(sourceInfo.hashes) + expect([...crlf.before.entries()]).toEqual([...sourceInfo.before.entries()]) + expect([...crlf.after.entries()]).toEqual([...sourceInfo.after.entries()]) + reports.push({ kind: 'synthetic-crlf-source-control', canonicalHashesMatch: true, reads }) +}) diff --git a/docs/audits/browser-closed-result-retention/source-versions.json b/docs/audits/browser-closed-result-retention/source-versions.json new file mode 100644 index 00000000000..3942a5e7a71 --- /dev/null +++ b/docs/audits/browser-closed-result-retention/source-versions.json @@ -0,0 +1,405 @@ +{ + "canonicalLF": true, + "sources": [ + { + "path": "src/main/browser/browser-client-host-command-dispatcher.ts", + "workingSha256": "f8c610a54f7d16c59043250dde5ef399f70e1a974c9492d09d403138e46c1da3", + "lineCount": 315, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "f8c610a54f7d16c59043250dde5ef399f70e1a974c9492d09d403138e46c1da3", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "f8c610a54f7d16c59043250dde5ef399f70e1a974c9492d09d403138e46c1da3", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/browser-client-host-command-result-cache.ts", + "workingSha256": "9ec3dad78c83f864f74957cad766cbcf41863e92d81d7e7a471702b0f9ab2f34", + "lineCount": 51, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "9ec3dad78c83f864f74957cad766cbcf41863e92d81d7e7a471702b0f9ab2f34", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "9ec3dad78c83f864f74957cad766cbcf41863e92d81d7e7a471702b0f9ab2f34", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/browser-client-host-command-state.ts", + "workingSha256": "a5f6b7a3737f86b2953fc43fa0e3ed5faaa141814d5f94fd192da1bb510f355f", + "lineCount": 171, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "a5f6b7a3737f86b2953fc43fa0e3ed5faaa141814d5f94fd192da1bb510f355f", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "a5f6b7a3737f86b2953fc43fa0e3ed5faaa141814d5f94fd192da1bb510f355f", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/browser-client-host-command-page.ts", + "workingSha256": "f5ca7f90511ee0290d2a1d0b3c5c39d3c9841ebcf90a5564bce83fbc9aca06bb", + "lineCount": 220, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "f5ca7f90511ee0290d2a1d0b3c5c39d3c9841ebcf90a5564bce83fbc9aca06bb", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "f5ca7f90511ee0290d2a1d0b3c5c39d3c9841ebcf90a5564bce83fbc9aca06bb", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/browser-client-host-command-join.ts", + "workingSha256": "5bf1a38231100cc416176ef2052bdff7b3fca91b6f6dbefcd222b92be59cbb2f", + "lineCount": 21, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "5bf1a38231100cc416176ef2052bdff7b3fca91b6f6dbefcd222b92be59cbb2f", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "5bf1a38231100cc416176ef2052bdff7b3fca91b6f6dbefcd222b92be59cbb2f", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/browser-client-page-command-executor.ts", + "workingSha256": "d249de82f89365371fa4f3b457e0da552910819555a5a0bced0c95010ccacb01", + "lineCount": 319, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "d249de82f89365371fa4f3b457e0da552910819555a5a0bced0c95010ccacb01", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "d249de82f89365371fa4f3b457e0da552910819555a5a0bced0c95010ccacb01", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/browser-client-page-command-execution.ts", + "workingSha256": "82b7c87d17b34c267ebfdf3d6ee25058cbd05dbc163ac24c43aefed00f0e3c6e", + "lineCount": 114, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "82b7c87d17b34c267ebfdf3d6ee25058cbd05dbc163ac24c43aefed00f0e3c6e", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "82b7c87d17b34c267ebfdf3d6ee25058cbd05dbc163ac24c43aefed00f0e3c6e", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/browser-client-page-command-executor-test-harness.ts", + "workingSha256": "b43ec764e4d094070fab51381ceb24487999dca97b4a38131be08fab0c5cbe24", + "lineCount": 145, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "b43ec764e4d094070fab51381ceb24487999dca97b4a38131be08fab0c5cbe24", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "b43ec764e4d094070fab51381ceb24487999dca97b4a38131be08fab0c5cbe24", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/browser-client-page-automation-runtime.ts", + "workingSha256": "7b1c0c527a87b8c22ea8290a2e1efc7ca659f2b4a5e2bd7c740fb5c22a2a6319", + "lineCount": 141, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "7b1c0c527a87b8c22ea8290a2e1efc7ca659f2b4a5e2bd7c740fb5c22a2a6319", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "7b1c0c527a87b8c22ea8290a2e1efc7ca659f2b4a5e2bd7c740fb5c22a2a6319", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/browser-route-guest-lifecycle.ts", + "workingSha256": "52adbea664bcfc5d774f129d442f4fc36d4a1e1280a073053fc09ae026ca83cb", + "lineCount": 172, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "52adbea664bcfc5d774f129d442f4fc36d4a1e1280a073053fc09ae026ca83cb", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "52adbea664bcfc5d774f129d442f4fc36d4a1e1280a073053fc09ae026ca83cb", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/browser-route-webcontents-registry.ts", + "workingSha256": "132c6740539aaeb9b1ceb3c108d2aebfb9e8e5c4ce3ff7cd1191e5b45dff8cad", + "lineCount": 325, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "132c6740539aaeb9b1ceb3c108d2aebfb9e8e5c4ce3ff7cd1191e5b45dff8cad", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "132c6740539aaeb9b1ceb3c108d2aebfb9e8e5c4ce3ff7cd1191e5b45dff8cad", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/paired-runtime-browser-client-host.ts", + "workingSha256": "797c0130e30c5f40551f5d392326fff1ac8752e262653b3f855dfd7df9fa0038", + "lineCount": 193, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "797c0130e30c5f40551f5d392326fff1ac8752e262653b3f855dfd7df9fa0038", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "797c0130e30c5f40551f5d392326fff1ac8752e262653b3f855dfd7df9fa0038", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/paired-runtime-browser-client-host-composition.ts", + "workingSha256": "f8b55a24035e929664f3c5f61ee8a81ee560250379dcf64b39d35e1b60628b74", + "lineCount": 323, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "f8b55a24035e929664f3c5f61ee8a81ee560250379dcf64b39d35e1b60628b74", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "f8b55a24035e929664f3c5f61ee8a81ee560250379dcf64b39d35e1b60628b74", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/paired-runtime-browser-client-host-teardown.ts", + "workingSha256": "4d9eb53c81ddfa3cb8181735b08c2b90a380bebab1f173ae5d8da9f719671f59", + "lineCount": 68, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "4d9eb53c81ddfa3cb8181735b08c2b90a380bebab1f173ae5d8da9f719671f59", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "4d9eb53c81ddfa3cb8181735b08c2b90a380bebab1f173ae5d8da9f719671f59", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/paired-runtime-browser-client-host-runtime.ts", + "workingSha256": "2d0650d0a5ebe88f1185ab5676762ed93188e7c28a69508137ed7cfa36e3832c", + "lineCount": 327, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "2d0650d0a5ebe88f1185ab5676762ed93188e7c28a69508137ed7cfa36e3832c", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "2d0650d0a5ebe88f1185ab5676762ed93188e7c28a69508137ed7cfa36e3832c", + "matchesWorking": true + } + } + }, + { + "path": "src/main/runtime/rpc/methods/browser-core.ts", + "workingSha256": "fdc2afeece6689644fbd607a59e484ef9d2ac6db7404e3378f2db10e16984f6d", + "lineCount": 293, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "fdc2afeece6689644fbd607a59e484ef9d2ac6db7404e3378f2db10e16984f6d", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "c847be873f4ce19b04796be962985a0234f159da2b38881fb8f6db1a1cbf720b", + "matchesWorking": false + } + } + }, + { + "path": "src/main/runtime/runtime-browser-commands-active-screencasts-by-page-id.ts", + "workingSha256": "0af16d6fe883f6af79754e064b77a1b9ab8e80953657434eacc88cc1c523e038", + "lineCount": 209, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "0af16d6fe883f6af79754e064b77a1b9ab8e80953657434eacc88cc1c523e038", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "0af16d6fe883f6af79754e064b77a1b9ab8e80953657434eacc88cc1c523e038", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/agent-browser-bridge-types.ts", + "workingSha256": "3ea69d15aef862807108835d1cbd536275a105a96bdc86f3be95dfbed55ab45f", + "lineCount": 65, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "3ea69d15aef862807108835d1cbd536275a105a96bdc86f3be95dfbed55ab45f", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "3ea69d15aef862807108835d1cbd536275a105a96bdc86f3be95dfbed55ab45f", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/agent-browser-bridge-raw-process.ts", + "workingSha256": "d0e9929c4a4571d8752efa5e4063500efa75fefd228eb6cb1ea6092748befb0c", + "lineCount": 108, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "d0e9929c4a4571d8752efa5e4063500efa75fefd228eb6cb1ea6092748befb0c", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "d0e9929c4a4571d8752efa5e4063500efa75fefd228eb6cb1ea6092748befb0c", + "matchesWorking": true + } + } + }, + { + "path": "src/main/browser/agent-browser-bridge-core-commands.ts", + "workingSha256": "d587a7e7c3bd368c9003802607edd20def98e64442cd6d5914779c633feebd03", + "lineCount": 169, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "d587a7e7c3bd368c9003802607edd20def98e64442cd6d5914779c633feebd03", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "06d6a61e431680ebd89e9a29b18e2198da3d84df6398b234fa8a255a6fcedf8a", + "matchesWorking": false + } + } + }, + { + "path": "src/main/startup/main-process-ready-runtime.ts", + "workingSha256": "5bb3615e9dc430e0a9ab61cb3d70e57ecbfa4c5ea82d5414fc82d93e81e9819b", + "lineCount": 156, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "5bb3615e9dc430e0a9ab61cb3d70e57ecbfa4c5ea82d5414fc82d93e81e9819b", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "5bb3615e9dc430e0a9ab61cb3d70e57ecbfa4c5ea82d5414fc82d93e81e9819b", + "matchesWorking": true + } + } + }, + { + "path": "src/shared/browser-client-host-protocol.ts", + "workingSha256": "712fe3eaed30c2de322466f316c966e526dcb60cf387cc0f6ba48012c70a7956", + "lineCount": 343, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "712fe3eaed30c2de322466f316c966e526dcb60cf387cc0f6ba48012c70a7956", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "712fe3eaed30c2de322466f316c966e526dcb60cf387cc0f6ba48012c70a7956", + "matchesWorking": true + } + } + }, + { + "path": "src/shared/browser-client-automation-protocol.ts", + "workingSha256": "f93331c39fd0f8e08518d7a11a461bf5108c1a65be8f213ab2e2eff2ab977e58", + "lineCount": 129, + "namedRefs": { + "mainCheckpoint": { + "ref": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "f93331c39fd0f8e08518d7a11a461bf5108c1a65be8f213ab2e2eff2ab977e58", + "matchesWorking": true + }, + "v1.4.198": { + "ref": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "f93331c39fd0f8e08518d7a11a461bf5108c1a65be8f213ab2e2eff2ab977e58", + "matchesWorking": true + } + } + } + ], + "baselineHashes": { + "src/main/browser/browser-client-host-command-dispatcher.ts": "f8c610a54f7d16c59043250dde5ef399f70e1a974c9492d09d403138e46c1da3", + "src/main/browser/browser-client-host-command-result-cache.ts": "9ec3dad78c83f864f74957cad766cbcf41863e92d81d7e7a471702b0f9ab2f34" + }, + "fixedHashes": { + "src/main/browser/browser-client-host-command-dispatcher.ts": "0e4074a4cb7379de58d75dd93e3f74090dea18a944bd4f038730cf19f6e68a98", + "src/main/browser/browser-client-host-command-result-cache.ts": "f06bd1f31be9ffcbb750f242bacd0c2f16222cde7d011010acbd46ceb70ea83f" + }, + "auditedHead": "6ab6802df90553d2a26e9a4c7a519ae11d4b5e09" +} diff --git a/docs/audits/browser-closed-result-retention/sources.cjs b/docs/audits/browser-closed-result-retention/sources.cjs new file mode 100644 index 00000000000..c05aa714517 --- /dev/null +++ b/docs/audits/browser-closed-result-retention/sources.cjs @@ -0,0 +1,42 @@ +const assert = require('node:assert/strict') +const { readFileSync } = require('node:fs') +const { createHash } = require('node:crypto') +const { resolve } = require('node:path') +const { applyPatch, parsePatch, reversePatch } = require('diff') + +const canonicalLf = (text) => text.replace(/\r\n/g, '\n') +const sha256 = (text) => createHash('sha256').update(text).digest('hex') + +function loadSources({ readText = (filename) => readFileSync(filename, 'utf8') } = {}) { + const root = resolve(__dirname, '../../..') + const expected = JSON.parse(readFileSync(resolve(__dirname, 'source-versions.json'), 'utf8')) + const patches = parsePatch(canonicalLf(readText(resolve(__dirname, 'fix.patch')))) + const before = new Map() + const after = new Map() + const hashes = {} + assert.equal(patches.length, 2) + for (const patch of patches) { + const path = patch.newFileName.replace(/^b\//, '') + assert.ok(Object.hasOwn(expected.baselineHashes, path), `Unexpected patch path: ${path}`) + const absolute = resolve(root, path) + const current = canonicalLf(readText(absolute)) + const baseline = applyPatch(current, reversePatch(patch)) + assert.notEqual(baseline, false, `Patch no longer reverses: ${path}`) + assert.equal(sha256(current), expected.fixedHashes[path], `Fixed source drift: ${path}`) + assert.equal(sha256(baseline), expected.baselineHashes[path], `Baseline source drift: ${path}`) + before.set(absolute, baseline) + after.set(absolute, current) + hashes[path] = { before: sha256(baseline), after: sha256(current) } + } + for (const source of expected.sources) { + if (Object.hasOwn(hashes, source.path)) { + continue + } + const text = canonicalLf(readText(resolve(root, source.path))) + assert.equal(sha256(text), source.workingSha256, `Caller source drift: ${source.path}`) + hashes[source.path] = { before: sha256(text), after: sha256(text) } + } + return { root, before, after, hashes } +} + +module.exports = { loadSources } diff --git a/docs/audits/browser-closed-result-retention/validation.json b/docs/audits/browser-closed-result-retention/validation.json new file mode 100644 index 00000000000..d793de73f51 --- /dev/null +++ b/docs/audits/browser-closed-result-retention/validation.json @@ -0,0 +1,228 @@ +{ + "tests": { + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-client-host-command-retention.test.ts src/main/browser/browser-client-host-command-dispatcher.test.ts src/main/browser/browser-client-page-command-executor.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts", + "passed": 77, + "files": 5, + "newRegressionCases": 2, + "exitCode": 0 + }, + "baselineOverlay": { + "command": "ORCA_BACKGROUND_LAUNCH=1 ORCA_BROWSER_CACHE_VARIANT=before pnpm exec vitest run --config docs/audits/browser-closed-result-retention/vitest.config.mjs src/main/browser/browser-client-host-command-retention.test.ts src/main/browser/browser-client-host-command-dispatcher.test.ts", + "passed": 16, + "expectedFailed": 2, + "failures": [ + "32 completed result objects remain reachable while native navigation stays pending.", + "The first late closed input remains reachable while a sibling handler stays pending." + ], + "exitCode": 1 + }, + "typecheck": { + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm tc:node", + "exitCode": 0 + }, + "publicationQuality": { + "scans": [ + { + "label": "code quality", + "command": [ + "pnpm", + "exec", + "oxlint", + "--no-ignore", + "--deny-warnings", + "--report-unused-disable-directives-severity", + "warn", + "src/main/browser/browser-client-host-command-dispatcher.ts", + "src/main/browser/browser-client-host-command-result-cache.ts", + "src/main/browser/browser-client-host-command-retention.test.ts", + "docs/audits/browser-closed-result-retention/sources.cjs", + "docs/audits/browser-closed-result-retention/vitest.config.mjs", + "docs/audits/browser-closed-result-retention/scenario.test.mjs" + ], + "exitCode": 0 + }, + { + "label": "casting code quality", + "command": [ + "pnpm", + "exec", + "oxlint", + "--no-ignore", + "--deny-warnings", + "--config", + "config/oxlint-code-quality-casting.json", + "src/main/browser/browser-client-host-command-dispatcher.ts", + "src/main/browser/browser-client-host-command-result-cache.ts", + "src/main/browser/browser-client-host-command-retention.test.ts", + "docs/audits/browser-closed-result-retention/sources.cjs", + "docs/audits/browser-closed-result-retention/vitest.config.mjs", + "docs/audits/browser-closed-result-retention/scenario.test.mjs" + ], + "exitCode": 0 + }, + { + "label": "type-aware code quality", + "command": [ + "pnpm", + "exec", + "oxlint", + "--no-ignore", + "--deny-warnings", + "--type-aware", + "--config", + "config/oxlint-code-quality-type-aware.json", + "src/main/browser/browser-client-host-command-dispatcher.ts", + "src/main/browser/browser-client-host-command-result-cache.ts", + "src/main/browser/browser-client-host-command-retention.test.ts", + "docs/audits/browser-closed-result-retention/sources.cjs", + "docs/audits/browser-closed-result-retention/vitest.config.mjs", + "docs/audits/browser-closed-result-retention/scenario.test.mjs" + ], + "exitCode": 0 + }, + { + "label": "React Doctor", + "command": [ + "pnpm", + "exec", + "oxlint", + "--no-ignore", + "--deny-warnings", + "--config", + "config/oxlint-react-doctor.json", + "src/main/browser/browser-client-host-command-dispatcher.ts", + "src/main/browser/browser-client-host-command-result-cache.ts", + "src/main/browser/browser-client-host-command-retention.test.ts", + "docs/audits/browser-closed-result-retention/sources.cjs", + "docs/audits/browser-closed-result-retention/vitest.config.mjs", + "docs/audits/browser-closed-result-retention/scenario.test.mjs" + ], + "exitCode": 0 + }, + { + "label": "design system", + "command": [ + "pnpm", + "exec", + "oxlint", + "--no-ignore", + "--deny-warnings", + "--config", + "config/oxlint-design-system.json", + "src/main/browser/browser-client-host-command-dispatcher.ts", + "src/main/browser/browser-client-host-command-result-cache.ts", + "src/main/browser/browser-client-host-command-retention.test.ts", + "docs/audits/browser-closed-result-retention/sources.cjs", + "docs/audits/browser-closed-result-retention/vitest.config.mjs", + "docs/audits/browser-closed-result-retention/scenario.test.mjs" + ], + "exitCode": 0 + } + ] + }, + "changedQuality": { + "command": "ORCA_BACKGROUND_LAUNCH=1 ORCA_CODE_QUALITY_BASE=HEAD pnpm run check:code-quality:changed", + "exitCode": 0, + "note": "The five explicit-file scans include all six TS/CJS/MJS publication paths. The ordinary changed gate does not see ignored new artifacts before staging." + }, + "proofs": { + "runs": [ + { + "runtime": "node", + "variant": "before", + "command": [ + "pnpm", + "exec", + "vitest", + "run", + "--config", + "docs/audits/browser-closed-result-retention/vitest.config.mjs", + "docs/audits/browser-closed-result-retention/scenario.test.mjs" + ], + "exitCode": 0 + }, + { + "runtime": "node", + "variant": "fixed", + "command": [ + "pnpm", + "exec", + "vitest", + "run", + "--config", + "docs/audits/browser-closed-result-retention/vitest.config.mjs", + "docs/audits/browser-closed-result-retention/scenario.test.mjs" + ], + "exitCode": 0 + }, + { + "runtime": "electron", + "variant": "before", + "command": [ + "node_modules/electron/dist/Electron.app/Contents/MacOS/Electron", + "node_modules/vitest/vitest.mjs", + "run", + "--config", + "docs/audits/browser-closed-result-retention/vitest.config.mjs", + "docs/audits/browser-closed-result-retention/scenario.test.mjs" + ], + "exitCode": 0 + }, + { + "runtime": "electron", + "variant": "fixed", + "command": [ + "node_modules/electron/dist/Electron.app/Contents/MacOS/Electron", + "node_modules/vitest/vitest.mjs", + "run", + "--config", + "docs/audits/browser-closed-result-retention/vitest.config.mjs", + "docs/audits/browser-closed-result-retention/scenario.test.mjs" + ], + "exitCode": 0 + } + ], + "casesPerVariantPerRuntime": 4, + "variants": ["before", "fixed"], + "node": "26.6.0", + "electron": "43.7.0", + "electronNode": "24.21.0", + "controlledPendingNativePorts": 1, + "smallCompletedResults": 32, + "crlfReadControl": 24, + "environment": { + "ORCA_BACKGROUND_LAUNCH": "1", + "ELECTRON_RUN_AS_NODE": "1 for Electron runs", + "ORCA_BROWSER_CACHE_VARIANT": "before or fixed" + }, + "outputOverride": "ORCA_BROWSER_CACHE_OUTPUT" + }, + "sourceParity": { + "canonicalLF": true, + "mainCheckpoint": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "historicalVersion": "v1.4.198", + "historicalRef": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "namedBaselineTargetMatches": 2, + "mainCheckpointCitedSourceMatches": 23, + "historicalCitedSourceMatches": 21, + "citedSourceCount": 23, + "hashCoverage": "Two product targets plus 21 cited caller/dependency modules, not all transitive imports.", + "historicalApplicationReplay": false + }, + "scope": { + "trigger": "A handler outlives the dispatcher close join (default 5 seconds).", + "nativeSettlementAuthorityPreserved": true, + "retirePageCachePolicyChanged": false, + "incidentAttribution": false, + "measuredRSS": false + }, + "format": "All 14 publication files except fix.patch checked with oxfmt stdin mode; a second pass produced identical bytes.", + "gitDiffCheckExitCode": 0, + "publicationWhitespace": { + "commandTemplate": "git diff --no-index --check ", + "files": 14, + "expectedExitCode": 1, + "diagnostics": 0, + "note": "The complete content of every publication path is checked, including ignored new artifacts. Exit 1 only means the content differs from an empty file. fix.patch uses zero-context hunks." + } +} diff --git a/docs/audits/browser-closed-result-retention/vitest.config.mjs b/docs/audits/browser-closed-result-retention/vitest.config.mjs new file mode 100644 index 00000000000..6622fc9269b --- /dev/null +++ b/docs/audits/browser-closed-result-retention/vitest.config.mjs @@ -0,0 +1,30 @@ +import { resolve } from 'node:path' +import { createRequire } from 'node:module' +import { defineConfig, mergeConfig } from 'vitest/config' +import base from '../../../config/vitest.config.ts' + +const { loadSources } = createRequire(import.meta.url)('./sources.cjs') +const { before, after } = loadSources() +const sources = process.env.ORCA_BROWSER_CACHE_VARIANT === 'before' ? before : after +const config = mergeConfig( + base, + defineConfig({ + plugins: [ + { + name: 'closed-browser-cache-source-overlay', + enforce: 'pre', + transform(_code, id) { + const source = sources.get(resolve(id.split('?')[0])) + return source === undefined ? undefined : { code: source, map: null } + } + } + ] + }) +) +config.test.include = [ + 'docs/audits/browser-closed-result-retention/scenario.test.mjs', + 'src/main/browser/browser-client-host-command-retention.test.ts', + 'src/main/browser/browser-client-host-command-dispatcher.test.ts' +] +config.test.maxWorkers = 1 +export default config diff --git a/src/main/browser/browser-client-host-command-dispatcher.ts b/src/main/browser/browser-client-host-command-dispatcher.ts index b79b0cbdcad..54140ff9585 100644 --- a/src/main/browser/browser-client-host-command-dispatcher.ts +++ b/src/main/browser/browser-client-host-command-dispatcher.ts @@ -163,6 +163,7 @@ export class BrowserClientHostCommandDispatcher { for (const page of this.pages.values()) { page.retiring = true this.cancelPage(page, 'browser_host_command_cancelled') + this.resultCache.releasePage(page) } const settled = await joinBrowserClientHostCommands( [...this.pages.values()].flatMap((page) => @@ -297,7 +298,7 @@ export class BrowserClientHostCommandDispatcher { private removeActiveRecord(page: PageState, record: CommandRecord): void { if (removeActiveCommandRecord(page, record)) { this.activeCommands -= 1 - this.resultCache.record(page, record) + this.resultCache.record(page, record, !this.closed) } } diff --git a/src/main/browser/browser-client-host-command-result-cache.ts b/src/main/browser/browser-client-host-command-result-cache.ts index 0a3796e6a92..59c6430244c 100644 --- a/src/main/browser/browser-client-host-command-result-cache.ts +++ b/src/main/browser/browser-client-host-command-result-cache.ts @@ -8,7 +8,11 @@ export class BrowserClientHostCommandResultCache { private readonly maxTotal: number ) {} - record(page: PageState, record: CommandRecord): void { + record(page: PageState, record: CommandRecord, retain = true): void { + if (!retain) { + this.evict(page, record.event.commandSequence, record) + return + } page.settledSequences.push(record.event.commandSequence) this.pagesByRecord.set(record, page) while (page.settledSequences.length > this.maxPerPage) { diff --git a/src/main/browser/browser-client-host-command-retention.test.ts b/src/main/browser/browser-client-host-command-retention.test.ts new file mode 100644 index 00000000000..1b00a40c330 --- /dev/null +++ b/src/main/browser/browser-client-host-command-retention.test.ts @@ -0,0 +1,201 @@ +import { expect, it } from 'vitest' +import type { + BrowserClientHostCommandEvent, + BrowserClientHostCommandResult, + BrowserClientHostLeaseAuthority +} from '../../shared/browser-client-host-protocol' +import { BrowserClientHostCommandDispatcher } from './browser-client-host-command-dispatcher' +import { BrowserClientPageCommandExecutor } from './browser-client-page-command-executor' +import { createCommand, createHarness } from './browser-client-page-command-executor-test-harness' +import { closeBrowserClientHostComposition } from './paired-runtime-browser-client-host-teardown' + +const authority: BrowserClientHostLeaseAuthority = { + authorityRuntimeId: 'runtime-a', + authorityEpoch: 'epoch-a', + browserHostClientId: 'client-a', + browserHostGeneration: 3, + pageCommandProtocolVersion: 1 +} + +function deferred(): { promise: Promise; resolve: (value: T) => void } { + let resolve = (_value: T): void => {} + const promise = new Promise((settle) => { + resolve = settle + }) + return { promise, resolve } +} + +async function collect(): Promise { + if (!global.gc) { + throw new Error('This retention test requires --expose-gc') + } + for (let turn = 0; turn < 8; turn += 1) { + await new Promise((resolve) => setImmediate(resolve)) + global.gc() + } +} + +function command( + sequence: number, + body: BrowserClientHostCommandEvent['command'], + page = 'page-a' +): BrowserClientHostCommandEvent { + return createCommand('createPage', { + browserPageId: page, + commandSequence: sequence, + commandId: `${page}-${sequence}`, + command: body + }) +} + +async function rememberResult( + dispatcher: BrowserClientHostCommandDispatcher, + sequence: number +): Promise> { + const result = await dispatcher.dispatch( + command(sequence, { type: 'automation', method: 'browser.snapshot', params: {} }) + ) + if (result.status !== 'completed' || typeof result.value !== 'object' || !result.value) { + throw new Error('Expected an object result') + } + return new WeakRef(result.value) +} + +function dispatchInput( + dispatcher: BrowserClientHostCommandDispatcher, + page: string +): { input: WeakRef; result: Promise } { + const params = { title: `small-input-${page}` } + return { + input: new WeakRef(params), + result: dispatcher.dispatch( + command(2, { type: 'automation', method: 'browser.snapshot', params }, page) + ) + } +} + +it('releases completed results on close while preserving pending native page custody', async () => { + const harness = createHarness() + const navigation = deferred() + const entered = deferred() + let nativeSignal: AbortSignal | undefined + let executorClosed = false + let deferredClose: Promise | undefined + let ordinal = 0 + const executor = new BrowserClientPageCommandExecutor({ + ...harness.dependencies, + executeAutomation: async () => ({ title: `small-result-${ordinal++}`, items: [1, 2, 3] }), + routeWebContents: { + ...harness.dependencies.routeWebContents, + navigateGuest: () => { + entered.resolve() + return navigation.promise + } + } + }) + const dispatcher = new BrowserClientHostCommandDispatcher({ + authority, + joinTimeoutMs: 15, + handler: (event, signal) => { + if (event.command.type === 'navigate') { + nativeSignal = signal + } + return executor.handle(event, signal) + } + }) + await dispatcher.dispatch(createCommand('createPage')) + const results: WeakRef[] = [] + for (let sequence = 2; sequence < 34; sequence += 1) { + results.push(await rememberResult(dispatcher, sequence)) + } + await collect() + expect(results.filter((result) => result.deref())).toHaveLength(32) + const pending = dispatcher.dispatch( + command(34, { type: 'navigate', url: 'https://example.invalid/held' }) + ) + await entered.promise + executor.fenceNavigation() + try { + const settled = await closeBrowserClientHostComposition({ + host: { + close: () => dispatcher.close(), + whenHandlersSettled: () => dispatcher.whenClosed() + }, + executor: { + async close() { + executorClosed = true + await executor.close() + } + }, + routeSets: { close: async () => {} }, + error: new Error('controlled disconnect'), + deferExecutorClose: (close) => { + deferredClose = close + }, + reportCleanupError: (error) => { + throw error + } + }) + expect(settled).toBe(false) + await expect(pending).resolves.toMatchObject({ errorCode: 'browser_host_command_cancelled' }) + expect(nativeSignal?.aborted).toBe(true) + expect(executorClosed).toBe(false) + expect(executor.hasPage('page-a', 7)).toBe(true) + expect(harness.route.release).not.toHaveBeenCalled() + expect(harness.routeSession.release).not.toHaveBeenCalled() + expect(() => dispatcher.dispatch(createCommand('createPage'))).toThrow('dispatcher_closed') + expect(await dispatcher.close()).toBe(false) + await collect() + expect(results.filter((result) => result.deref())).toHaveLength(0) + } finally { + navigation.resolve(true) + await dispatcher.whenClosed() + await deferredClose + await executor.close() + } + expect(executorClosed).toBe(true) + expect(harness.route.release).toHaveBeenCalledOnce() + expect(harness.routeSession.release).toHaveBeenCalledOnce() +}) + +it('discards late closed records while retaining a sibling pending handler', async () => { + const first = deferred() + const second = deferred() + const dispatcher = new BrowserClientHostCommandDispatcher({ + authority, + joinTimeoutMs: 15, + handler: (event) => + event.command.type === 'createPage' + ? { status: 'completed' } + : event.browserPageId === 'page-a' + ? first.promise + : second.promise + }) + for (const page of ['page-a', 'page-b']) { + await dispatcher.dispatch( + command( + 1, + { type: 'createPage', browserProfileId: 'profile-a', executionHostKey: 'execution-host-a' }, + page + ) + ) + } + const a = dispatchInput(dispatcher, 'page-a') + const b = dispatchInput(dispatcher, 'page-b') + try { + expect(await dispatcher.close()).toBe(false) + await expect(a.result).resolves.toMatchObject({ errorCode: 'browser_host_command_cancelled' }) + await expect(b.result).resolves.toMatchObject({ errorCode: 'browser_host_command_cancelled' }) + first.resolve({ status: 'completed' }) + await collect() + expect(a.input.deref()).toBeUndefined() + expect(b.input.deref()).toBeDefined() + expect(await dispatcher.close()).toBe(false) + } finally { + first.resolve({ status: 'completed' }) + second.resolve({ status: 'completed' }) + await dispatcher.whenClosed() + } + await collect() + expect(b.input.deref()).toBeUndefined() +}) From 9ed2f743a4dd7aab1d35906c049c11d143a92aaf Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:19:51 -0700 Subject: [PATCH 062/168] fix(runtime): fence terminal snapshot completion by owner (#20996) Co-authored-by: m4air --- .../headless-hydration-retention/README.md | 39 ++++ .../headless-hydration-retention/fix.patch | 158 ++++++++++++++ .../reproduce.mjs | 130 +++++++++++ .../headless-hydration-retention/results.json | 51 +++++ ...adless-hydration-ownership-test-fixture.ts | 82 +++++++ .../headless-hydration-ownership.test.ts | 125 +++++++++++ .../runtime/headless-seed-ownership.test.ts | 202 ++++++++++++++++++ ...untime-capture-provider-terminal-buffer.ts | 11 +- ...time-create-pty-headless-terminal-state.ts | 12 +- ...me-maybe-hydrate-headless-from-renderer.ts | 16 +- ...-runtime-serialize-main-terminal-buffer.ts | 9 + 11 files changed, 827 insertions(+), 8 deletions(-) create mode 100644 docs/audits/headless-hydration-retention/README.md create mode 100644 docs/audits/headless-hydration-retention/fix.patch create mode 100644 docs/audits/headless-hydration-retention/reproduce.mjs create mode 100644 docs/audits/headless-hydration-retention/results.json create mode 100644 src/main/runtime/headless-hydration-ownership-test-fixture.ts create mode 100644 src/main/runtime/headless-hydration-ownership.test.ts create mode 100644 src/main/runtime/headless-seed-ownership.test.ts diff --git a/docs/audits/headless-hydration-retention/README.md b/docs/audits/headless-hydration-retention/README.md new file mode 100644 index 00000000000..7ab6ace96a8 --- /dev/null +++ b/docs/audits/headless-hydration-retention/README.md @@ -0,0 +1,39 @@ +# Late headless snapshot ownership + +## Reproduced defect + +The runtime can retire a PTY or replace its headless model while a renderer/provider snapshot or emulator seed write is pending. The old completion still updates maps keyed only by PTY ID. After exit cleanup, a successful renderer reply recreates CWD, recent-output and title state; even an empty or rejected reply recreates the hydration `done` entry. A replaced model can also lose its provider preference or pending hydration status to the old completion. + +Provider snapshot validation has two related races. Its late generation check calls an allocating getter, recreating an entry that exit just deleted. Its cleanup can delete a newer capture's live-mode scanner Set after the old Set becomes empty. Provider tail parsing has the same allocating late check after an awaited parse/write. + +These paths exist in `v1.4.198`. They explain a concrete main/runtime retention mechanism under PTY churn, but the frequency and retained size in #19831/#19768 remain unproven. The renderer request already has a 750 ms timeout (`src/main/ipc/pty/ipc/serialize-buffer.ts`); this fix addresses callbacks writing after their owner retires, not an indefinite renderer wait. + +## Fix and ownership + +The three model-seeding paths compare `headlessTerminals.get(ptyId)` with the captured state before starting work and after asynchronous boundaries. Completion bookkeeping runs only for that state. Provider capture/tail completion compares the existing generation without allocating, and capture cleanup removes the map entry only while it still owns the same Set. + +Admission generation allocation and normal queued live writes keep their existing behavior. Disposal still drains live writes queued before retirement. Current hydration, query replay suppression, CWD/kitty metadata and replacement capture mode tracking remain covered. These are local runtime ownership checks for both local and SSH-backed terminals; they add no process-death inference, remote cancellation or wire change. They do not depend on git worktrees. + +## Reproduce + +Run from the repository root with installed dependencies: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node docs/audits/headless-hydration-retention/reproduce.mjs +``` + +The script runs the actual `OrcaRuntimeService` regression fixtures twice. For the before case, it reverses only the included four-file `fix.patch` in a temporary Vite transform. It neither rewrites source files nor needs an unpublished commit. The after case uses the checked-out source. Source hashes and individual failing cases are recorded in `results.json`. + +- Before: **16 failed, 6 passed**. +- After: **22 passed**. + +The tests control pending promises to cover retirement before callback admission, during renderer/provider replies, during seed writes and during kitty metadata application. They cover success, null, rejection, same-ID replacement, current-state success, generation preservation and live-mode scanner ownership. Provider-tail checks exercise both normal and visible-screen-only parsing. + +Additional validation: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node node_modules/typescript/bin/tsc --noEmit -p config/tsconfig.node.json +ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/runtime/orca-runtime.test.ts src/main/runtime/terminal-query-responder.test.ts src/main/runtime/headless-hydration-ownership.test.ts src/main/runtime/headless-seed-ownership.test.ts --testNamePattern 'headless|hydrat|WSL|provider cwd|live WSL cwd|query|seed|retire|replacement|capture|renderer' +``` + +Node typecheck passed. The selected existing runtime/query checks plus the new cases passed **306 tests**, with 1087 unrelated cases skipped by the name filter. All runs were headless and used `ORCA_BACKGROUND_LAUNCH=1`. diff --git a/docs/audits/headless-hydration-retention/fix.patch b/docs/audits/headless-hydration-retention/fix.patch new file mode 100644 index 00000000000..dfa283eae32 --- /dev/null +++ b/docs/audits/headless-hydration-retention/fix.patch @@ -0,0 +1,158 @@ +diff --git a/src/main/runtime/orca-runtime-capture-provider-terminal-buffer.ts b/src/main/runtime/orca-runtime-capture-provider-terminal-buffer.ts +index acf4f60532..fc9ba31964 100644 +--- a/src/main/runtime/orca-runtime-capture-provider-terminal-buffer.ts ++++ b/src/main/runtime/orca-runtime-capture-provider-terminal-buffer.ts +@@ -31,7 +31,7 @@ export class OrcaRuntimeWithCaptureProviderTerminalBuffer extends OrcaRuntimeWit + // Why: daemon PTYs survive an app relaunch before any renderer mounts. + // Mobile still needs their retained history without navigating desktop. + const snapshot = await this.ptyController?.serializeProviderBuffer?.(ptyId, opts) +- if (!snapshot || this.getPtyLifecycleGeneration(ptyId) !== generation) { ++ if (!snapshot || this.ptyLifecycleGenerationById.get(ptyId) !== generation) { + return null + } + const snapshotModeTracker = new TerminalKittyKeyboardModeTracker() +@@ -73,7 +73,10 @@ export class OrcaRuntimeWithCaptureProviderTerminalBuffer extends OrcaRuntimeWit + return null + } finally { + liveModeTrackers.delete(liveModeTracker) +- if (liveModeTrackers.size === 0) { ++ if ( ++ liveModeTrackers.size === 0 && ++ this.providerModeSnapshotScansByPtyId.get(ptyId) === liveModeTrackers ++ ) { + this.providerModeSnapshotScansByPtyId.delete(ptyId) + } + } +@@ -163,7 +166,7 @@ export class OrcaRuntimeWithCaptureProviderTerminalBuffer extends OrcaRuntimeWit + if (snapshotOptions.visibleScreenOnly) { + const projection = await this.parseVisibleSnapshot(snapshot) + // Live bytes ordered after the provider frame make that frame stale. +- return this.getPtyLifecycleGeneration(ptyId) === generation && ++ return this.ptyLifecycleGenerationById.get(ptyId) === generation && + this.getPtyOutputSequence(ptyId) <= snapshot.seq + ? projection + : { lines: [] } +@@ -180,7 +183,7 @@ export class OrcaRuntimeWithCaptureProviderTerminalBuffer extends OrcaRuntimeWit + try { + await emulator.write(data) + const projection = projectTerminalTailLines(emulator, lineLimit) +- return this.getPtyLifecycleGeneration(ptyId) === generation && ++ return this.ptyLifecycleGenerationById.get(ptyId) === generation && + this.getPtyOutputSequence(ptyId) <= snapshot.seq + ? projection + : { lines: [] } +diff --git a/src/main/runtime/orca-runtime-create-pty-headless-terminal-state.ts b/src/main/runtime/orca-runtime-create-pty-headless-terminal-state.ts +index da5d824ef6..b9f3547850 100644 +--- a/src/main/runtime/orca-runtime-create-pty-headless-terminal-state.ts ++++ b/src/main/runtime/orca-runtime-create-pty-headless-terminal-state.ts +@@ -112,8 +112,11 @@ export class OrcaRuntimeWithCreatePtyHeadlessTerminalState extends OrcaRuntimeWi + this.headlessTerminals.set(ptyId, state) + state.writeChain = state.writeChain + .then(async () => { ++ if (this.headlessTerminals.get(ptyId) !== state) { ++ return ++ } + const snapshot = await this.serializeProviderTerminalBuffer(ptyId) +- if (!snapshot) { ++ if (this.headlessTerminals.get(ptyId) !== state || !snapshot) { + return + } + const data = `${snapshot.scrollbackAnsi ?? ''}${snapshot.data}` +@@ -123,6 +126,9 @@ export class OrcaRuntimeWithCreatePtyHeadlessTerminalState extends OrcaRuntimeWi + this.recordOsc7MetadataForPty(ptyId, data) + } + await state.emulator.write(data) ++ if (this.headlessTerminals.get(ptyId) !== state) { ++ return ++ } + if (snapshot.cwd !== undefined) { + state.emulator.setCwd(snapshot.cwd) + if (!this.terminalCwdByPtyId.has(ptyId) && snapshot.cwd?.trim()) { +@@ -141,7 +147,9 @@ export class OrcaRuntimeWithCreatePtyHeadlessTerminalState extends OrcaRuntimeWi + // Best-effort: live bytes already chain behind this replacement state. + }) + .finally(() => { +- this.providerSnapshotPreferredPtys.delete(ptyId) ++ if (this.headlessTerminals.get(ptyId) === state) { ++ this.providerSnapshotPreferredPtys.delete(ptyId) ++ } + }) + } + +diff --git a/src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts b/src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts +index 163ffaecdb..63135f261b 100644 +--- a/src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts ++++ b/src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts +@@ -51,6 +51,9 @@ export class OrcaRuntimeWithMaybeHydrateHeadlessFromRenderer extends OrcaRuntime + // setting headlessTerminals, the live byte would lazy-create a separate + // state and the seed-resolve would overwrite it, dropping live bytes. + state.writeChain = state.writeChain.then(async () => { ++ if (this.headlessTerminals.get(ptyId) !== state) { ++ return ++ } + try { + // Why the scrollback is not suppressed mid-TUI: the seed IS the model's + // normal buffer, so zeroing it while an alt-screen agent was up left the +@@ -58,7 +61,11 @@ export class OrcaRuntimeWithMaybeHydrateHeadlessFromRenderer extends OrcaRuntime + const rendered = await controller.serializeBuffer!(ptyId, { + scrollbackRows: MOBILE_SUBSCRIBE_SCROLLBACK_ROWS + }) +- if (!rendered || rendered.data.length === 0) { ++ if ( ++ this.headlessTerminals.get(ptyId) !== state || ++ !rendered || ++ rendered.data.length === 0 ++ ) { + return + } + this.recordOsc7MetadataForPty(ptyId, rendered.data) +@@ -70,6 +77,9 @@ export class OrcaRuntimeWithMaybeHydrateHeadlessFromRenderer extends OrcaRuntime + state.emulator.resize(rendered.cols, rendered.rows) + } + await state.emulator.write(rendered.data) ++ if (this.headlessTerminals.get(ptyId) !== state) { ++ return ++ } + const ptyDims = this.getTerminalSize(ptyId) + if (ptyDims && (ptyDims.cols !== rendered.cols || ptyDims.rows !== rendered.rows)) { + state.emulator.resize(ptyDims.cols, ptyDims.rows) +@@ -91,7 +101,9 @@ export class OrcaRuntimeWithMaybeHydrateHeadlessFromRenderer extends OrcaRuntime + // Hydration is best-effort. Live writes continue via the same + // writeChain that this catch-arm leaves intact. + } finally { +- this.headlessHydrationState.set(ptyId, 'done') ++ if (this.headlessTerminals.get(ptyId) === state) { ++ this.headlessHydrationState.set(ptyId, 'done') ++ } + } + }) + } +diff --git a/src/main/runtime/orca-runtime-serialize-main-terminal-buffer.ts b/src/main/runtime/orca-runtime-serialize-main-terminal-buffer.ts +index 6559dbfd34..5b6da61d14 100644 +--- a/src/main/runtime/orca-runtime-serialize-main-terminal-buffer.ts ++++ b/src/main/runtime/orca-runtime-serialize-main-terminal-buffer.ts +@@ -134,15 +134,24 @@ export class OrcaRuntimeWithSerializeMainTerminalBuffer extends OrcaRuntimeWithA + this.recordRecentPtyOutputForPathProvenance(ptyId, data) + state.writeChain = state.writeChain + .then(async () => { ++ if (this.headlessTerminals.get(ptyId) !== state) { ++ return ++ } + // Why: seed writes never set forwardQueryReplies — the main-side + // replay guard. A snapshot containing old queries must answer no one. + await state.emulator.write(data) ++ if (this.headlessTerminals.get(ptyId) !== state) { ++ return ++ } + // Why AFTER the seed write: the snapshot payload cannot carry kitty + // pushes (rehydrateSequences deliberately omits them), but ordering + // behind it keeps the parse deterministic. Unflagged like the seed — + // re-applying flags must answer no one. + if (typeof metadata.kittyKeyboardFlags === 'number') { + await state.emulator.applyKittyKeyboardFlags(metadata.kittyKeyboardFlags) ++ if (this.headlessTerminals.get(ptyId) !== state) { ++ return ++ } + } + if (metadata.cwd !== undefined) { + state.emulator.setCwd(metadata.cwd) diff --git a/docs/audits/headless-hydration-retention/reproduce.mjs b/docs/audits/headless-hydration-retention/reproduce.mjs new file mode 100644 index 00000000000..dc7bb72bac0 --- /dev/null +++ b/docs/audits/headless-hydration-retention/reproduce.mjs @@ -0,0 +1,130 @@ +import { createHash } from 'node:crypto' +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { createRequire } from 'node:module' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import { applyPatch, parsePatch, reversePatch } from 'diff' +import { build } from 'esbuild' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1.') +} + +const root = fileURLToPath(new URL('../../../', import.meta.url)) +const patch = await readFile(new URL('./fix.patch', import.meta.url), 'utf8') +const beforeSources = {} +const sourceHashes = {} +for (const parsed of parsePatch(patch)) { + const path = parsed.newFileName.replace(/^b\//, '') + const absolute = resolve(root, path) + const current = await readFile(absolute, 'utf8') + const before = applyPatch(current, reversePatch(parsed)) + if (before === false) { + throw new Error(`Source changed; review the proof patch: ${path}`) + } + beforeSources[absolute.replaceAll('\\', '/')] = before + sourceHashes[path] = { + before: createHash('sha256').update(before).digest('hex'), + after: createHash('sha256').update(current).digest('hex') + } +} + +const scratch = await mkdtemp(join(tmpdir(), 'orca-hydration-retention-')) +const require = createRequire(import.meta.url) +let runnerModuleId +try { + const runnerPath = join(scratch, 'run-process.cjs') + await build({ + absWorkingDir: root, + entryPoints: [resolve(root, 'src/shared/child-process/run-process.ts')], + outfile: runnerPath, + bundle: true, + platform: 'node', + format: 'cjs', + logLevel: 'silent' + }) + runnerModuleId = require.resolve(runnerPath) + const { runProcess } = require(runnerModuleId) + const baselineConfig = join(scratch, 'before.config.mjs') + const configImport = JSON.stringify(pathToFileURL(resolve(root, 'config/vitest.config.ts')).href) + await writeFile( + baselineConfig, + `import base from ${configImport}; +const beforeSources = ${JSON.stringify(beforeSources)}; +export default {...base, plugins: [{ + name: 'hydrate-before-ownership-fix', enforce: 'pre', + transform(_code, id) { + const before = beforeSources[id.replaceAll('\\\\', '/').split('?')[0]]; + return before === undefined ? null : {code: before, map: null}; + } +}]};\n` + ) + + async function run(label, config) { + const report = join(scratch, `${label}.json`) + const result = await runProcess({ + program: process.execPath, + args: [ + resolve(root, 'node_modules/vitest/vitest.mjs'), + 'run', + '--config', + config, + 'src/main/runtime/headless-hydration-ownership.test.ts', + 'src/main/runtime/headless-seed-ownership.test.ts', + '--reporter=json', + `--outputFile=${report}` + ], + cwd: root, + env: process.env, + timeoutMs: 90_000, + maxOutputBytes: 4 * 1024 * 1024 + }) + let parsed + try { + parsed = JSON.parse(await readFile(report, 'utf8')) + } catch (error) { + throw new Error(`${label} runner failed: ${result.stderr || result.stdout}`, { cause: error }) + } + return { + exitCode: result.code, + passed: parsed.numPassedTests, + failed: parsed.numFailedTests, + failedCases: parsed.testResults.flatMap((suite) => + suite.assertionResults + .filter((test) => test.status === 'failed') + .map((test) => test.fullName) + ) + } + } + + const before = await run('before', baselineConfig) + const after = await run('after', resolve(root, 'config/vitest.config.ts')) + const passed = + before.failed > 0 && + before.passed + before.failed === 22 && + after.passed === 22 && + after.failed === 0 + console.log( + JSON.stringify( + { + comparison: + 'Actual runtime tests; before reverses only fix.patch in a temporary Vite transform', + sourceHashes, + before, + after, + passed + }, + null, + 2 + ) + ) + if (!passed) { + process.exitCode = 1 + } +} finally { + if (runnerModuleId) { + delete require.cache[runnerModuleId] + } + await rm(scratch, { recursive: true, force: true }) +} diff --git a/docs/audits/headless-hydration-retention/results.json b/docs/audits/headless-hydration-retention/results.json new file mode 100644 index 00000000000..38a45b7cbab --- /dev/null +++ b/docs/audits/headless-hydration-retention/results.json @@ -0,0 +1,51 @@ +{ + "comparison": "Actual runtime tests; before reverses only fix.patch in a temporary Vite transform", + "sourceHashes": { + "src/main/runtime/orca-runtime-capture-provider-terminal-buffer.ts": { + "before": "53e8db1da23a2048abe498b4ea9911ad176460322c2cb6da2fcfd5472e6b4b2d", + "after": "9df660b042323ad7e68e093add5b3fbdbfd47f80976710bc657ab16471f07a02" + }, + "src/main/runtime/orca-runtime-create-pty-headless-terminal-state.ts": { + "before": "67222b470b133cb473acf85e10de970535cceacf06964a5afeb50d640a0fb0ac", + "after": "21a9aef91c4debaf91aa2bd3a7d7b77bc46769cc7cead0609b8425350ea78636" + }, + "src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts": { + "before": "dd0d060f53512c4f1cddd1f3dbe4cb86d11d2d90fecba41c01f936d2d2344aff", + "after": "5ba68552f81a9dfba1e4f8756732b958a91ecf60b14a62c1c536cca61c5c55e7" + }, + "src/main/runtime/orca-runtime-serialize-main-terminal-buffer.ts": { + "before": "c30adce84d951030366b5954a7ad59dcbc902f021f83906d7b58bd90eb7c5a1b", + "after": "c5c533376402d7963360813aebf01299d4e3e950e4945852ddea20d88f3cdbfc" + } + }, + "before": { + "exitCode": 1, + "passed": 6, + "failed": 16, + "failedCases": [ + "does not start renderer hydration after the model retires before its callback", + "does not resurrect retired renderer-hydration state after success", + "does not resurrect retired renderer-hydration state after null", + "does not resurrect retired renderer-hydration state after reject", + "keeps a same-ID replacement pending when an old renderer snapshot arrives", + "skips late title and completion bookkeeping after disposal during the seed write", + "skips an initial seed retired before its callback without clearing the replacement preference", + "keeps replacement ownership when an initial seed awaits write", + "keeps replacement ownership when an initial seed awaits kitty", + "does not acquire a provider snapshot for a model retired before its callback", + "does not retain provider state after a retired acquisition returns success", + "refuses a stale context seed after model replacement within the same PTY generation", + "does not reinsert provider CWD after disposal during its seed write", + "keeps the replacement capture generation and live-mode scan after an old capture settles", + "does not remint a retired generation after parsing a provider tail, visible-only: false", + "does not remint a retired generation after parsing a provider tail, visible-only: true" + ] + }, + "after": { + "exitCode": 0, + "passed": 22, + "failed": 0, + "failedCases": [] + }, + "passed": true +} diff --git a/src/main/runtime/headless-hydration-ownership-test-fixture.ts b/src/main/runtime/headless-hydration-ownership-test-fixture.ts new file mode 100644 index 00000000000..5fb3314419c --- /dev/null +++ b/src/main/runtime/headless-hydration-ownership-test-fixture.ts @@ -0,0 +1,82 @@ +import { afterEach, vi } from 'vitest' +import './orca-runtime-test-lifecycle.spec' +import { OrcaRuntimeService } from './orca-runtime' +import { store, syncSinglePty } from './orca-runtime-test-fixtures.spec' + +export const PTY_ID = 'pty-hydration-owner' +export const SIZE = { cols: 80, rows: 24 } +export const RETIRED_SNAPSHOT = { + data: '\x1b]7;file:///retired-context\x07RETIRED-SEED', + lastTitle: 'Codex working', + ...SIZE +} + +export class HydrationRuntime extends OrcaRuntimeService { + model() { + const state = this.headlessTerminals.get(PTY_ID) + if (!state) { + throw new Error('Expected headless model') + } + return state + } + + retainedState() { + return { + model: this.headlessTerminals.has(PTY_ID), + hydration: this.headlessHydrationState.get(PTY_ID), + cwd: this.terminalCwdByPtyId.get(PTY_ID), + titleTracker: this.ptyTitleTrackersByPtyId.has(PTY_ID), + recentOutput: this.recentPtyOutputById.has(PTY_ID), + providerPreferred: this.providerSnapshotPreferredPtys.has(PTY_ID), + generation: this.ptyLifecycleGenerationById.get(PTY_ID), + snapshotScans: this.providerModeSnapshotScansByPtyId.get(PTY_ID)?.size ?? 0 + } + } + + preferProvider() { + this.providerSnapshotPreferredPtys.add(PTY_ID) + } + + replaceExecutionContext() { + this.replaceHeadlessTerminalAfterExecutionContextChange(PTY_ID) + } + + captureProvider() { + return this.captureProviderTerminalBuffer(PTY_ID, {}, this.getPtyLifecycleGeneration(PTY_ID)) + } + + providerTail(visibleScreenOnly: boolean) { + return this.readProviderTerminalTailLines(PTY_ID, 10, { visibleScreenOnly }) + } +} + +const runtimes: HydrationRuntime[] = [] + +export function createHydrationRuntime(): HydrationRuntime { + const runtime = new HydrationRuntime(store) + syncSinglePty(runtime, PTY_ID) + runtimes.push(runtime) + return runtime +} + +export function retire(runtime: HydrationRuntime): void { + runtime.onPtyExit(PTY_ID, 0, undefined, { providerExitObserved: true }) +} + +export const EMPTY_RETAINED_STATE = { + model: false, + hydration: undefined, + cwd: undefined, + titleTracker: false, + recentOutput: false, + providerPreferred: false, + generation: undefined, + snapshotScans: 0 +} + +afterEach(() => { + for (const runtime of runtimes.splice(0)) { + retire(runtime) + } + vi.restoreAllMocks() +}) diff --git a/src/main/runtime/headless-hydration-ownership.test.ts b/src/main/runtime/headless-hydration-ownership.test.ts new file mode 100644 index 00000000000..675a57bf70b --- /dev/null +++ b/src/main/runtime/headless-hydration-ownership.test.ts @@ -0,0 +1,125 @@ +import { expect, it, vi } from 'vitest' +import { deferred, makeDeferred } from './orca-runtime-test-fixtures.spec' +import { + createHydrationRuntime, + EMPTY_RETAINED_STATE, + PTY_ID, + RETIRED_SNAPSHOT, + retire, + SIZE +} from './headless-hydration-ownership-test-fixture' + +type Snapshot = typeof RETIRED_SNAPSHOT | null + +function prepare() { + const runtime = createHydrationRuntime() + const snapshot = deferred() + const serialize = vi.fn(() => snapshot.promise) + runtime.setPtyController({ + write: () => true, + kill: () => true, + getForegroundProcess: async () => null, + getSize: () => SIZE, + hasRendererSerializer: () => true, + serializeBuffer: serialize + }) + return { runtime, snapshot, serialize } +} + +it('does not start renderer hydration after the model retires before its callback', async () => { + const { runtime, snapshot, serialize } = prepare() + runtime.onPtyData(PTY_ID, 'queued-live', 1) + const old = runtime.model() + const write = vi.spyOn(old.emulator, 'write') + retire(runtime) + snapshot.resolve(RETIRED_SNAPSHOT) + await old.writeChain + expect(serialize).not.toHaveBeenCalled() + expect(write).toHaveBeenCalledWith('queued-live', { forwardQueryReplies: false }) + expect(runtime.retainedState()).toEqual(EMPTY_RETAINED_STATE) +}) + +it.each(['success', 'null', 'reject'] as const)( + 'does not resurrect retired renderer-hydration state after %s', + async (outcome) => { + const { runtime, snapshot, serialize } = prepare() + runtime.onPtyData(PTY_ID, 'queued-live', 1) + const old = runtime.model() + await vi.waitFor(() => expect(serialize).toHaveBeenCalledOnce()) + retire(runtime) + if (outcome === 'reject') { + snapshot.reject(new Error('Renderer unavailable')) + } else { + snapshot.resolve(outcome === 'success' ? RETIRED_SNAPSHOT : null) + } + await old.writeChain + expect(runtime.retainedState()).toEqual(EMPTY_RETAINED_STATE) + } +) + +it.each(['success', 'null', 'reject'] as const)( + 'settles current renderer hydration after %s and preserves queued live bytes', + async (outcome) => { + const { runtime, snapshot } = prepare() + runtime.onPtyData(PTY_ID, 'CURRENT-LIVE', 1) + const current = runtime.model() + if (outcome === 'reject') { + snapshot.reject(new Error('Renderer unavailable')) + } else { + snapshot.resolve(outcome === 'success' ? RETIRED_SNAPSHOT : null) + } + await current.writeChain + expect(runtime.retainedState().hydration).toBe('done') + expect(current.emulator.getVisibleLines().join('\n')).toContain('CURRENT-LIVE') + expect(current.emulator.getVisibleLines().join('\n').includes('RETIRED-SEED')).toBe( + outcome === 'success' + ) + } +) + +it('keeps a same-ID replacement pending when an old renderer snapshot arrives', async () => { + const { runtime, snapshot, serialize } = prepare() + runtime.onPtyData(PTY_ID, 'OLD-LIVE', 1) + const old = runtime.model() + await vi.waitFor(() => expect(serialize).toHaveBeenCalledOnce()) + runtime.notePtyDataGap(PTY_ID) + const replacementSnapshot = deferred() + serialize.mockImplementation(() => replacementSnapshot.promise) + runtime.onPtyData(PTY_ID, 'NEW-LIVE', 2) + const replacement = runtime.model() + runtime.preferProvider() + await vi.waitFor(() => expect(serialize).toHaveBeenCalledTimes(2)) + snapshot.resolve(RETIRED_SNAPSHOT) + await old.writeChain + expect(runtime.model()).toBe(replacement) + expect(runtime.retainedState()).toMatchObject({ hydration: 'pending', providerPreferred: true }) + expect(runtime.retainedState().cwd).toBeUndefined() + replacementSnapshot.resolve({ ...RETIRED_SNAPSHOT, data: 'NEW-SEED', lastTitle: 'New title' }) + await replacement.writeChain + const text = replacement.emulator.getVisibleLines().join('\n') + expect(text).toContain('NEW-SEEDNEW-LIVE') + expect(text).not.toContain('OLD-LIVE') + expect(text).not.toContain('RETIRED-SEED') + expect(runtime.retainedState()).toMatchObject({ hydration: 'done', providerPreferred: false }) +}) + +it('skips late title and completion bookkeeping after disposal during the seed write', async () => { + const { runtime, snapshot, serialize } = prepare() + runtime.onPtyData(PTY_ID, 'queued-live', 1) + const old = runtime.model() + const started = makeDeferred() + const release = makeDeferred() + const original = old.emulator.write.bind(old.emulator) + vi.spyOn(old.emulator, 'write').mockImplementationOnce(async (data) => { + started.resolve() + await release.promise + return original(data) + }) + await vi.waitFor(() => expect(serialize).toHaveBeenCalledOnce()) + snapshot.resolve(RETIRED_SNAPSHOT) + await started.promise + retire(runtime) + release.resolve() + await old.writeChain + expect(runtime.retainedState()).toEqual(EMPTY_RETAINED_STATE) +}) diff --git a/src/main/runtime/headless-seed-ownership.test.ts b/src/main/runtime/headless-seed-ownership.test.ts new file mode 100644 index 00000000000..39963e531d2 --- /dev/null +++ b/src/main/runtime/headless-seed-ownership.test.ts @@ -0,0 +1,202 @@ +import { expect, it, vi } from 'vitest' +import { HeadlessEmulator } from '../daemon/headless-emulator' +import { deferred, makeDeferred, syncSinglePty } from './orca-runtime-test-fixtures.spec' +import type { PtyProviderBufferSnapshot } from '../providers/types' +import { + createHydrationRuntime, + EMPTY_RETAINED_STATE, + PTY_ID, + retire, + SIZE +} from './headless-hydration-ownership-test-fixture' + +const PROVIDER_SNAPSHOT: PtyProviderBufferSnapshot = { + ...SIZE, + data: 'PROVIDER-SEED', + cwd: '/retired-context', + seq: 0, + source: 'headless', + alternateScreen: false +} + +function prepareProvider() { + const runtime = createHydrationRuntime() + const snapshot = deferred() + const serialize = vi.fn(() => snapshot.promise) + runtime.setPtyController({ + write: () => true, + kill: () => true, + getForegroundProcess: async () => null, + getSize: () => SIZE, + serializeProviderBuffer: serialize + }) + return { runtime, snapshot, serialize } +} + +it('skips an initial seed retired before its callback without clearing the replacement preference', async () => { + const runtime = createHydrationRuntime() + runtime.seedHeadlessTerminal(PTY_ID, 'OLD-SEED') + const old = runtime.model() + const write = vi.spyOn(old.emulator, 'write') + runtime.notePtyDataGap(PTY_ID) + runtime.onPtyData(PTY_ID, 'NEW-LIVE', 1) + const replacement = runtime.model() + runtime.preferProvider() + await old.writeChain + await replacement.writeChain + expect(write).not.toHaveBeenCalled() + expect(runtime.retainedState().providerPreferred).toBe(true) + expect(replacement.emulator.getVisibleLines().join('\n')).toContain('NEW-LIVE') +}) + +it.each(['write', 'kitty'] as const)( + 'keeps replacement ownership when an initial seed awaits %s', + async (stage) => { + const runtime = createHydrationRuntime() + runtime.seedHeadlessTerminal(PTY_ID, 'OLD-SEED', SIZE, { kittyKeyboardFlags: 3 }) + const old = runtime.model() + const started = makeDeferred() + const release = makeDeferred() + if (stage === 'write') { + const original = old.emulator.write.bind(old.emulator) + vi.spyOn(old.emulator, 'write').mockImplementationOnce(async (data) => { + started.resolve() + await release.promise + return original(data) + }) + } else { + const original = old.emulator.applyKittyKeyboardFlags.bind(old.emulator) + vi.spyOn(old.emulator, 'applyKittyKeyboardFlags').mockImplementationOnce(async (flags) => { + started.resolve() + await release.promise + return original(flags) + }) + } + await started.promise + runtime.notePtyDataGap(PTY_ID) + runtime.onPtyData(PTY_ID, 'NEW-LIVE', 1) + runtime.preferProvider() + release.resolve() + await old.writeChain + expect(runtime.retainedState().providerPreferred).toBe(true) + } +) + +it('preserves current seed metadata and ordered live output', async () => { + const runtime = createHydrationRuntime() + runtime.seedHeadlessTerminal(PTY_ID, 'SEED-', SIZE, { cwd: '/current', kittyKeyboardFlags: 3 }) + runtime.onPtyData(PTY_ID, 'LIVE', 1) + await runtime.model().writeChain + const snapshot = runtime.model().emulator.getSnapshot() + expect(snapshot.snapshotAnsi).toContain('SEED-LIVE') + expect(snapshot.cwd).toBe('/current') + expect(snapshot.modes.kittyKeyboardFlags).toBe(3) +}) + +it('does not acquire a provider snapshot for a model retired before its callback', async () => { + const { runtime, snapshot, serialize } = prepareProvider() + runtime.replaceExecutionContext() + const old = runtime.model() + retire(runtime) + snapshot.resolve(PROVIDER_SNAPSHOT) + await old.writeChain + expect(serialize).not.toHaveBeenCalled() + expect(runtime.retainedState()).toEqual(EMPTY_RETAINED_STATE) +}) + +it.each(['success', 'null', 'reject'] as const)( + 'does not retain provider state after a retired acquisition returns %s', + async (outcome) => { + const { runtime, snapshot, serialize } = prepareProvider() + runtime.replaceExecutionContext() + const old = runtime.model() + await vi.waitFor(() => expect(serialize).toHaveBeenCalledOnce()) + retire(runtime) + if (outcome === 'reject') { + snapshot.reject(new Error('Provider unavailable')) + } else { + snapshot.resolve(outcome === 'success' ? PROVIDER_SNAPSHOT : null) + } + await old.writeChain + expect(runtime.retainedState()).toEqual(EMPTY_RETAINED_STATE) + } +) + +it('refuses a stale context seed after model replacement within the same PTY generation', async () => { + const { runtime, snapshot, serialize } = prepareProvider() + runtime.replaceExecutionContext() + const old = runtime.model() + await vi.waitFor(() => expect(serialize).toHaveBeenCalledOnce()) + runtime.notePtyDataGap(PTY_ID) + runtime.onPtyData(PTY_ID, 'NEW-LIVE', 1) + const replacement = runtime.model() + runtime.preferProvider() + snapshot.resolve(PROVIDER_SNAPSHOT) + await old.writeChain + expect(runtime.model()).toBe(replacement) + expect(runtime.retainedState()).toMatchObject({ cwd: undefined, providerPreferred: true }) +}) + +it('does not reinsert provider CWD after disposal during its seed write', async () => { + const { runtime, snapshot } = prepareProvider() + runtime.replaceExecutionContext() + const old = runtime.model() + const started = makeDeferred() + const release = makeDeferred() + const original = old.emulator.write.bind(old.emulator) + vi.spyOn(old.emulator, 'write').mockImplementationOnce(async (data) => { + started.resolve() + await release.promise + return original(data) + }) + snapshot.resolve(PROVIDER_SNAPSHOT) + await started.promise + retire(runtime) + release.resolve() + await old.writeChain + expect(runtime.retainedState()).toEqual(EMPTY_RETAINED_STATE) +}) + +it('keeps the replacement capture generation and live-mode scan after an old capture settles', async () => { + const { runtime, snapshot, serialize } = prepareProvider() + const old = runtime.captureProvider() + const oldGeneration = runtime.retainedState().generation + retire(runtime) + syncSinglePty(runtime, PTY_ID) + const replacementSnapshot = deferred() + serialize.mockImplementation(() => replacementSnapshot.promise) + const replacement = runtime.captureProvider() + const newGeneration = runtime.retainedState().generation + expect(newGeneration).not.toBe(oldGeneration) + snapshot.resolve(PROVIDER_SNAPSHOT) + await expect(old).resolves.toBeNull() + expect(runtime.retainedState()).toMatchObject({ generation: newGeneration, snapshotScans: 1 }) + runtime.onPtyData(PTY_ID, '\x1b[?1049h', 1) + replacementSnapshot.resolve(PROVIDER_SNAPSHOT) + await expect(replacement).resolves.toMatchObject({ alternateScreen: true }) + expect(runtime.retainedState().snapshotScans).toBe(0) +}) + +it.each([false, true])( + 'does not remint a retired generation after parsing a provider tail, visible-only: %s', + async (visibleOnly) => { + const { runtime, snapshot } = prepareProvider() + const started = makeDeferred() + const release = makeDeferred() + const original = HeadlessEmulator.prototype.write + vi.spyOn(HeadlessEmulator.prototype, 'write').mockImplementationOnce( + async function (this: HeadlessEmulator, data, options) { + started.resolve() + await release.promise + return original.call(this, data, options) + } + ) + const read = runtime.providerTail(visibleOnly) + snapshot.resolve(PROVIDER_SNAPSHOT) + await started.promise + retire(runtime) + release.resolve() + await expect(read).resolves.toEqual({ lines: [] }) + expect(runtime.retainedState()).toEqual(EMPTY_RETAINED_STATE) + } +) diff --git a/src/main/runtime/orca-runtime-capture-provider-terminal-buffer.ts b/src/main/runtime/orca-runtime-capture-provider-terminal-buffer.ts index acf4f60532a..fc9ba31964a 100644 --- a/src/main/runtime/orca-runtime-capture-provider-terminal-buffer.ts +++ b/src/main/runtime/orca-runtime-capture-provider-terminal-buffer.ts @@ -31,7 +31,7 @@ export class OrcaRuntimeWithCaptureProviderTerminalBuffer extends OrcaRuntimeWit // Why: daemon PTYs survive an app relaunch before any renderer mounts. // Mobile still needs their retained history without navigating desktop. const snapshot = await this.ptyController?.serializeProviderBuffer?.(ptyId, opts) - if (!snapshot || this.getPtyLifecycleGeneration(ptyId) !== generation) { + if (!snapshot || this.ptyLifecycleGenerationById.get(ptyId) !== generation) { return null } const snapshotModeTracker = new TerminalKittyKeyboardModeTracker() @@ -73,7 +73,10 @@ export class OrcaRuntimeWithCaptureProviderTerminalBuffer extends OrcaRuntimeWit return null } finally { liveModeTrackers.delete(liveModeTracker) - if (liveModeTrackers.size === 0) { + if ( + liveModeTrackers.size === 0 && + this.providerModeSnapshotScansByPtyId.get(ptyId) === liveModeTrackers + ) { this.providerModeSnapshotScansByPtyId.delete(ptyId) } } @@ -163,7 +166,7 @@ export class OrcaRuntimeWithCaptureProviderTerminalBuffer extends OrcaRuntimeWit if (snapshotOptions.visibleScreenOnly) { const projection = await this.parseVisibleSnapshot(snapshot) // Live bytes ordered after the provider frame make that frame stale. - return this.getPtyLifecycleGeneration(ptyId) === generation && + return this.ptyLifecycleGenerationById.get(ptyId) === generation && this.getPtyOutputSequence(ptyId) <= snapshot.seq ? projection : { lines: [] } @@ -180,7 +183,7 @@ export class OrcaRuntimeWithCaptureProviderTerminalBuffer extends OrcaRuntimeWit try { await emulator.write(data) const projection = projectTerminalTailLines(emulator, lineLimit) - return this.getPtyLifecycleGeneration(ptyId) === generation && + return this.ptyLifecycleGenerationById.get(ptyId) === generation && this.getPtyOutputSequence(ptyId) <= snapshot.seq ? projection : { lines: [] } diff --git a/src/main/runtime/orca-runtime-create-pty-headless-terminal-state.ts b/src/main/runtime/orca-runtime-create-pty-headless-terminal-state.ts index da5d824ef62..b9f35478505 100644 --- a/src/main/runtime/orca-runtime-create-pty-headless-terminal-state.ts +++ b/src/main/runtime/orca-runtime-create-pty-headless-terminal-state.ts @@ -112,8 +112,11 @@ export class OrcaRuntimeWithCreatePtyHeadlessTerminalState extends OrcaRuntimeWi this.headlessTerminals.set(ptyId, state) state.writeChain = state.writeChain .then(async () => { + if (this.headlessTerminals.get(ptyId) !== state) { + return + } const snapshot = await this.serializeProviderTerminalBuffer(ptyId) - if (!snapshot) { + if (this.headlessTerminals.get(ptyId) !== state || !snapshot) { return } const data = `${snapshot.scrollbackAnsi ?? ''}${snapshot.data}` @@ -123,6 +126,9 @@ export class OrcaRuntimeWithCreatePtyHeadlessTerminalState extends OrcaRuntimeWi this.recordOsc7MetadataForPty(ptyId, data) } await state.emulator.write(data) + if (this.headlessTerminals.get(ptyId) !== state) { + return + } if (snapshot.cwd !== undefined) { state.emulator.setCwd(snapshot.cwd) if (!this.terminalCwdByPtyId.has(ptyId) && snapshot.cwd?.trim()) { @@ -141,7 +147,9 @@ export class OrcaRuntimeWithCreatePtyHeadlessTerminalState extends OrcaRuntimeWi // Best-effort: live bytes already chain behind this replacement state. }) .finally(() => { - this.providerSnapshotPreferredPtys.delete(ptyId) + if (this.headlessTerminals.get(ptyId) === state) { + this.providerSnapshotPreferredPtys.delete(ptyId) + } }) } diff --git a/src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts b/src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts index 163ffaecdb3..63135f261b7 100644 --- a/src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts +++ b/src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts @@ -51,6 +51,9 @@ export class OrcaRuntimeWithMaybeHydrateHeadlessFromRenderer extends OrcaRuntime // setting headlessTerminals, the live byte would lazy-create a separate // state and the seed-resolve would overwrite it, dropping live bytes. state.writeChain = state.writeChain.then(async () => { + if (this.headlessTerminals.get(ptyId) !== state) { + return + } try { // Why the scrollback is not suppressed mid-TUI: the seed IS the model's // normal buffer, so zeroing it while an alt-screen agent was up left the @@ -58,7 +61,11 @@ export class OrcaRuntimeWithMaybeHydrateHeadlessFromRenderer extends OrcaRuntime const rendered = await controller.serializeBuffer!(ptyId, { scrollbackRows: MOBILE_SUBSCRIBE_SCROLLBACK_ROWS }) - if (!rendered || rendered.data.length === 0) { + if ( + this.headlessTerminals.get(ptyId) !== state || + !rendered || + rendered.data.length === 0 + ) { return } this.recordOsc7MetadataForPty(ptyId, rendered.data) @@ -70,6 +77,9 @@ export class OrcaRuntimeWithMaybeHydrateHeadlessFromRenderer extends OrcaRuntime state.emulator.resize(rendered.cols, rendered.rows) } await state.emulator.write(rendered.data) + if (this.headlessTerminals.get(ptyId) !== state) { + return + } const ptyDims = this.getTerminalSize(ptyId) if (ptyDims && (ptyDims.cols !== rendered.cols || ptyDims.rows !== rendered.rows)) { state.emulator.resize(ptyDims.cols, ptyDims.rows) @@ -91,7 +101,9 @@ export class OrcaRuntimeWithMaybeHydrateHeadlessFromRenderer extends OrcaRuntime // Hydration is best-effort. Live writes continue via the same // writeChain that this catch-arm leaves intact. } finally { - this.headlessHydrationState.set(ptyId, 'done') + if (this.headlessTerminals.get(ptyId) === state) { + this.headlessHydrationState.set(ptyId, 'done') + } } }) } diff --git a/src/main/runtime/orca-runtime-serialize-main-terminal-buffer.ts b/src/main/runtime/orca-runtime-serialize-main-terminal-buffer.ts index 6559dbfd349..5b6da61d149 100644 --- a/src/main/runtime/orca-runtime-serialize-main-terminal-buffer.ts +++ b/src/main/runtime/orca-runtime-serialize-main-terminal-buffer.ts @@ -134,15 +134,24 @@ export class OrcaRuntimeWithSerializeMainTerminalBuffer extends OrcaRuntimeWithA this.recordRecentPtyOutputForPathProvenance(ptyId, data) state.writeChain = state.writeChain .then(async () => { + if (this.headlessTerminals.get(ptyId) !== state) { + return + } // Why: seed writes never set forwardQueryReplies — the main-side // replay guard. A snapshot containing old queries must answer no one. await state.emulator.write(data) + if (this.headlessTerminals.get(ptyId) !== state) { + return + } // Why AFTER the seed write: the snapshot payload cannot carry kitty // pushes (rehydrateSequences deliberately omits them), but ordering // behind it keeps the parse deterministic. Unflagged like the seed — // re-applying flags must answer no one. if (typeof metadata.kittyKeyboardFlags === 'number') { await state.emulator.applyKittyKeyboardFlags(metadata.kittyKeyboardFlags) + if (this.headlessTerminals.get(ptyId) !== state) { + return + } } if (metadata.cwd !== undefined) { state.emulator.setCwd(metadata.cwd) From 54500a4281f97e434940dc4a27ce5352b6796641 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:21:45 -0700 Subject: [PATCH 063/168] Release hang watchdog quit listener on shutdown (#20910) Co-authored-by: m4air Co-authored-by: m4air --- src/main/hang-watchdog/main-thread-hang-watchdog.test.ts | 6 +++++- src/main/hang-watchdog/main-thread-hang-watchdog.ts | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/hang-watchdog/main-thread-hang-watchdog.test.ts b/src/main/hang-watchdog/main-thread-hang-watchdog.test.ts index eaaea54225b..cd3811b19c4 100644 --- a/src/main/hang-watchdog/main-thread-hang-watchdog.test.ts +++ b/src/main/hang-watchdog/main-thread-hang-watchdog.test.ts @@ -10,7 +10,8 @@ const { workerState, appMock } = vi.hoisted(() => ({ appMock: { isPackaged: true, getAppPath: vi.fn(() => '/apps/orca/app.asar'), - on: vi.fn() + on: vi.fn(), + off: vi.fn() } })) @@ -58,6 +59,7 @@ describe('installMainThreadHangWatchdog', () => { workerState.instance = null workerState.error = null appMock.on.mockReset() + appMock.off.mockReset() appMock.isPackaged = true delete process.env.ORCA_HANG_WATCHDOG_FORCE delete process.env.ORCA_HANG_WATCHDOG_TIMEOUT_MS @@ -137,6 +139,7 @@ describe('installMainThreadHangWatchdog', () => { handle?.stop() expect(worker.postMessage.mock.calls.some(([m]) => m.type === 'shutdown')).toBe(true) + expect(appMock.off).toHaveBeenCalledWith('will-quit', expect.any(Function)) handle?.stop() const shutdowns = worker.postMessage.mock.calls.filter(([m]) => m.type === 'shutdown') @@ -173,6 +176,7 @@ describe('installMainThreadHangWatchdog', () => { const exitListener = worker.once.mock.calls.find(([event]) => event === 'exit')?.[1] expect(exitListener).toEqual(expect.any(Function)) exitListener() + expect(appMock.off).toHaveBeenCalledWith('will-quit', expect.any(Function)) vi.advanceTimersByTime(6_000) expect(worker.postMessage).not.toHaveBeenCalled() }) diff --git a/src/main/hang-watchdog/main-thread-hang-watchdog.ts b/src/main/hang-watchdog/main-thread-hang-watchdog.ts index 46454429fc7..5e6155b6be7 100644 --- a/src/main/hang-watchdog/main-thread-hang-watchdog.ts +++ b/src/main/hang-watchdog/main-thread-hang-watchdog.ts @@ -73,11 +73,15 @@ export function installMainThreadHangWatchdog(options: { return } stopped = true + // Drop the app-level callback as soon as this watchdog is retired so a + // closed worker cannot keep its closure (and worker handle) alive. + app.off('will-quit', stop) clearInterval(heartbeatTimer) postMessage({ type: 'shutdown' }) } worker.once('exit', () => { stopped = true + app.off('will-quit', stop) clearInterval(heartbeatTimer) }) worker.unref() From a0371806303c78755ede4461510098eabde0490b Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:25:19 -0700 Subject: [PATCH 064/168] fix(ai-vault): release retired search write fences (#20986) * fix(ai-vault): release retired search write fences * test(ai-vault): use checked search writer mocks * test: use typed access in memory retention regressions --------- Co-authored-by: m4air Co-authored-by: m4air --- .../session-search-write-fences/README.md | 49 ++++ .../session-search-write-fences/reproduce.mjs | 108 +++++++++ .../session-search-write-fences/results.json | 23 ++ .../session-search-index-consumer.ts | 3 + .../session-search-index-writer.test.ts | 40 ++-- .../session-search-index-writer.ts | 65 ++++-- .../ai-vault-search/session-search-store.ts | 1 + .../session-search-write-lifetime.test.ts | 209 ++++++++++++++++++ 8 files changed, 460 insertions(+), 38 deletions(-) create mode 100644 docs/audits/session-search-write-fences/README.md create mode 100644 docs/audits/session-search-write-fences/reproduce.mjs create mode 100644 docs/audits/session-search-write-fences/results.json create mode 100644 src/main/ai-vault-search/session-search-write-lifetime.test.ts diff --git a/docs/audits/session-search-write-fences/README.md b/docs/audits/session-search-write-fences/README.md new file mode 100644 index 00000000000..c5637e732b1 --- /dev/null +++ b/docs/audits/session-search-write-fences/README.md @@ -0,0 +1,49 @@ +# Session-search write fence retention + +The search writer remembered every removed path for its lifetime. Those counters +fenced a read whose source disappeared before its first commit: both the original +and deleted database cursors are absent, so comparing cursors alone cannot detect +the removal. Counters for paths with no remaining reads were never released. + +The fix tracks only active reads. Removal marks their captured lifetime as removed +and releases the path from the map immediately. New reads get a fresh lifetime; +cleanup from an older read cannot delete it. Final commit and explicit discard +release ownership, while intermediate chunk commits keep it. Consumer errors, +incomplete reads, throwing error reporters, and store close release their fences. + +## Reproduce + +From the repository root with dependencies installed and a Node version providing +`node:sqlite`: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --max-old-space-size=128 docs/audits/session-search-write-fences/reproduce.mjs +``` + +The script bundles the actual writer twice, using the production SQLite schema and +adapter. The baseline is published commit +`243f4431557471daa05636aed1a30be790485eda` (#20551), whose writer matches the pre-fix +source. The after version is the working tree. Only this named Git object is read; +the script fetches nothing. Results include both source hashes and runtime details. + +After 1,000 complete index/retire cycles: + +| Source | Remaining file rows | Retained path entries | Never-indexed stale commit accepted | +| ------ | ------------------: | --------------------: | ----------------------------------- | +| Before | 0 | 1,000 | No | +| After | 0 | 0 | No | + +The regression suite additionally drives the actual consumer/channel path, checks +intermediate flush ownership, failed/incomplete reads, throwing error reporters, +new-generation protection, idempotent discard, and close: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/ai-vault-search/session-search-write-lifetime.test.ts src/main/ai-vault-search/session-search-file-write.test.ts src/main/ai-vault-search/session-search-index-writer.test.ts src/main/ai-vault-search/session-search-index-consumer.test.ts +``` + +Existing retry bookkeeping can recreate a failed `files` metadata row when a +removed read finishes. Its session/messages stay absent and it cannot restore +searchable content. This patch preserves that status policy. + +This is current-code path metadata in the scanner child. The search writer did not +exist in `v1.4.198`; this finding does not explain the reported #19831/#19768 build. diff --git a/docs/audits/session-search-write-fences/reproduce.mjs b/docs/audits/session-search-write-fences/reproduce.mjs new file mode 100644 index 00000000000..531e93b2854 --- /dev/null +++ b/docs/audits/session-search-write-fences/reproduce.mjs @@ -0,0 +1,108 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { createHash } from 'node:crypto' +import { readFileSync } from 'node:fs' +import Module from 'node:module' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { build } from 'esbuild' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1') +} + +const root = fileURLToPath(new URL('../../../', import.meta.url)) +const sourcePath = 'src/main/ai-vault-search/session-search-index-writer.ts' +const baseline = '243f4431557471daa05636aed1a30be790485eda' +const before = execFileSync('git', ['show', `${baseline}:${sourcePath}`], { + cwd: root, + encoding: 'utf8', + maxBuffer: 1024 * 1024 +}) +const after = readFileSync(join(root, sourcePath), 'utf8') + +async function run(version, source) { + const built = await build({ + absWorkingDir: root, + stdin: { + contents: `export { SessionSearchIndexWriter } from './${sourcePath}'; +export { openSessionSearchDatabase } from './src/main/ai-vault-search/session-search-schema.ts';`, + resolveDir: root + }, + bundle: true, + platform: 'node', + format: 'cjs', + packages: 'external', + write: false, + plugins: [ + { + name: 'select-writer-version', + setup(bundler) { + bundler.onLoad({ filter: /session-search-index-writer\.ts$/ }, () => ({ + contents: source, + loader: 'ts' + })) + } + } + ] + }) + const compiled = new Module(join(root, 'session-search-write-fence-probe.cjs')) + compiled.filename = join(root, 'session-search-write-fence-probe.cjs') + compiled.paths = Module._nodeModulePaths(root) + compiled._compile(built.outputFiles[0].text, compiled.filename) + const db = compiled.exports.openSessionSearchDatabase(':memory:') + const writer = new compiled.exports.SessionSearchIndexWriter(db) + const candidate = (path) => ({ + agent: 'claude', + codexHome: null, + file: { path, mtimeMs: 1, modifiedAt: new Date(1).toISOString(), sizeBytes: 1 } + }) + const outcome = { session: null, byteOffset: 1, incomplete: false } + const tracked = version === 'before' ? writer.removals : writer.activeWrites + assert.ok(tracked instanceof Map) + try { + for (let index = 0; index < 1000; index++) { + const path = join('synthetic', `retired-${index}.jsonl`) + const write = writer.beginWrite(candidate(path), 'replace', 0) + assert.equal(write.commit(outcome), true) + writer.removeFile(path) + } + const retainedPathsAfterRetirement = tracked.size + const remainingFiles = db.prepare('SELECT count(*) AS count FROM files').get().count + assert.equal(retainedPathsAfterRetirement, version === 'before' ? 1000 : 0) + assert.equal(remainingFiles, 0) + const removed = join('synthetic', 'never-indexed.jsonl') + const stale = writer.beginWrite(candidate(removed), 'replace', 0) + writer.removeFile(removed) + const staleCommitAccepted = stale.commit(outcome) + assert.equal(staleCommitAccepted, false) + assert.equal(db.prepare('SELECT count(*) AS count FROM files').get().count, 0) + return { + source: version === 'before' ? baseline : 'working tree', + sourceSha256: createHash('sha256').update(source).digest('hex'), + retiredFiles: 1000, + remainingFiles, + retainedPathsAfterRetirement, + neverIndexedStaleCommitAccepted: staleCommitAccepted + } + } finally { + writer.close?.() + db.close() + } +} + +console.log( + JSON.stringify( + { + node: process.version, + platform: process.platform, + architecture: process.arch, + sourcePath, + database: 'Production schema and adapter with an in-memory SQLite database', + before: await run('before', before), + after: await run('after', after) + }, + null, + 2 + ) +) diff --git a/docs/audits/session-search-write-fences/results.json b/docs/audits/session-search-write-fences/results.json new file mode 100644 index 00000000000..471e56fc419 --- /dev/null +++ b/docs/audits/session-search-write-fences/results.json @@ -0,0 +1,23 @@ +{ + "node": "v26.6.0", + "platform": "darwin", + "architecture": "arm64", + "sourcePath": "src/main/ai-vault-search/session-search-index-writer.ts", + "database": "Production schema and adapter with an in-memory SQLite database", + "before": { + "source": "243f4431557471daa05636aed1a30be790485eda", + "sourceSha256": "cd636c04251ab98038d696acc8c75a330f79b413657a1dba57a766bd1356017d", + "retiredFiles": 1000, + "remainingFiles": 0, + "retainedPathsAfterRetirement": 1000, + "neverIndexedStaleCommitAccepted": false + }, + "after": { + "source": "working tree", + "sourceSha256": "a130b93059cf6d19814b998c73b56615058eae08c9a84b148cd819d06a04bc1b", + "retiredFiles": 1000, + "remainingFiles": 0, + "retainedPathsAfterRetirement": 0, + "neverIndexedStaleCommitAccepted": false + } +} diff --git a/src/main/ai-vault-search/session-search-index-consumer.ts b/src/main/ai-vault-search/session-search-index-consumer.ts index a2c0b3b8836..1bad60a9eb2 100644 --- a/src/main/ai-vault-search/session-search-index-consumer.ts +++ b/src/main/ai-vault-search/session-search-index-consumer.ts @@ -77,6 +77,7 @@ class SessionSearchReadConsumer implements TranscriptReadConsumer { // keeps the whole read on one path — the buffer is dropped and the file is // re-read. this.failed = true + this.write.discard() this.store.reportWriteFailure(error) } } @@ -90,6 +91,8 @@ class SessionSearchReadConsumer implements TranscriptReadConsumer { committed = !this.failed && !outcome.incomplete && this.write.commit(outcome) } catch (error) { this.store.reportWriteFailure(error) + } finally { + this.write.discard() } if (committed) { this.store.writeCommitted(candidate) diff --git a/src/main/ai-vault-search/session-search-index-writer.test.ts b/src/main/ai-vault-search/session-search-index-writer.test.ts index 1be12e35bc7..b53393e0cd6 100644 --- a/src/main/ai-vault-search/session-search-index-writer.test.ts +++ b/src/main/ai-vault-search/session-search-index-writer.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, expect, it } from 'vitest' +import { afterEach, beforeEach, expect, it, vi } from 'vitest' import { SessionSearchIndexConsumer } from './session-search-index-consumer' import { openSessionSearchIndexFile, @@ -25,6 +25,7 @@ beforeEach(async () => { }) afterEach(async () => { + vi.restoreAllMocks() store.close() await index.close() }) @@ -111,15 +112,13 @@ it('refuses to commit a write whose file was removed mid-read', () => { it('declines a behind cursor in beginRead before it ever reaches the store', () => { const attempted: number[] = [] - const stub = { - indexedFile: () => ({ byteOffset: 100, mtimeMs: 1, sizeBytes: 1 }), - beginWrite: (_candidate: unknown, _mode: unknown, previousByteOffset: number) => { - attempted.push(previousByteOffset) - return { add: () => undefined, commit: () => true } - }, - setFileState: () => undefined - } as unknown as SessionSearchStore - const consumer = new SessionSearchIndexConsumer(stub) + vi.spyOn(store, 'indexedFile').mockReturnValue({ byteOffset: 100, mtimeMs: 1, sizeBytes: 1 }) + vi.spyOn(store, 'beginWrite').mockImplementation((_candidate, _mode, previousByteOffset) => { + attempted.push(previousByteOffset) + return { add: () => undefined, commit: () => true, discard: () => undefined } + }) + vi.spyOn(store, 'setFileState').mockImplementation(() => undefined) + const consumer = new SessionSearchIndexConsumer(store) expect( consumer.beginRead({ @@ -142,22 +141,17 @@ it('declines a behind cursor in beginRead before it ever reaches the store', () it("hands the read's identity accessor to the store", () => { const captured: unknown[] = [] - const stub = { - indexedFile: () => null, - beginWrite: ( - _candidate: unknown, - _mode: unknown, - _previousByteOffset: unknown, - identity: unknown - ) => { + vi.spyOn(store, 'indexedFile').mockReturnValue(null) + vi.spyOn(store, 'beginWrite').mockImplementation( + (_candidate, _mode, _previousByteOffset, identity) => { captured.push(identity) - return { add: () => undefined, commit: () => true } - }, - setFileState: () => undefined - } as unknown as SessionSearchStore + return { add: () => undefined, commit: () => true, discard: () => undefined } + } + ) + vi.spyOn(store, 'setFileState').mockImplementation(() => undefined) const identity = (): null => null - new SessionSearchIndexConsumer(stub).beginRead({ + new SessionSearchIndexConsumer(store).beginRead({ candidate: syntheticCandidate(), mode: 'replace', previousByteOffset: 0, diff --git a/src/main/ai-vault-search/session-search-index-writer.ts b/src/main/ai-vault-search/session-search-index-writer.ts index 5e29f2004af..82c9e679af6 100644 --- a/src/main/ai-vault-search/session-search-index-writer.ts +++ b/src/main/ai-vault-search/session-search-index-writer.ts @@ -71,20 +71,20 @@ export type SessionSearchFileWrite = { */ add(message: TranscriptMessage): void /** - * Writes this file's rows, its session and its cursor in one transaction. + * Finishes this read, writing its rows, session and cursor in one transaction. * False when the file's record changed under this read — it was removed, or * another writer moved the cursor these rows continue from. A read that never * calls this leaves the index exactly as it found it, unless it chunked. */ commit(outcome: TranscriptReadOutcome): boolean + /** Ends an incomplete or failed read without publishing its buffered rows. */ + discard(): void } export class SessionSearchIndexWriter { private readonly records: SessionSearchFileRecords - // Removals per path, so a write can prove its source was not dropped under it - // rather than infer it from the cursor. In memory is enough: one process owns - // the index, and a removal only has to fence writes this process opened. - private readonly removals = new Map() + private readonly activeWrites = new Map() + private closed = false constructor( private readonly db: SyncDatabase, @@ -144,6 +144,9 @@ export class SessionSearchIndexWriter { previousByteOffset: number, identity?: () => TranscriptSessionIdentity | null ): SessionSearchFileWrite | null { + if (this.closed) { + return null + } const path = candidate.file.path const cursor = this.cursor(path) if (mode === 'append') { @@ -170,7 +173,11 @@ export class SessionSearchIndexWriter { * that is still in flight is fenced by the cursor its commit re-reads. */ removeFile(path: string): void { - this.removals.set(path, (this.removals.get(path) ?? 0) + 1) + const active = this.activeWrites.get(path) + if (active) { + active.removed = true + this.activeWrites.delete(path) + } const cursor = this.cursor(path) this.db.exec('BEGIN IMMEDIATE') try { @@ -183,6 +190,14 @@ export class SessionSearchIndexWriter { } } + close(): void { + this.closed = true + for (const active of this.activeWrites.values()) { + active.removed = true + } + this.activeWrites.clear() + } + private cursor(path: string): FileCursor | undefined { return this.db .prepare('SELECT session_row_id,byte_offset FROM files WHERE path = ?') @@ -204,11 +219,26 @@ export class SessionSearchIndexWriter { // these rows no longer continue anything, and committing on top of that // would resurrect a deleted source or duplicate a span. let expected = opened - const removalsAtStart = this.removals.get(path) ?? 0 // The session row is reused across re-reads of one file, so a `replace` // swaps a session's rows rather than minting a second generation of it. let session = opened?.session_row_id ?? null let hash = append && session !== null ? this.records.contentHash(session) : EMPTY_CONTENT_HASH + const lifetime = this.activeWrites.get(path) ?? { removed: false, readers: 0 } + lifetime.readers++ + this.activeWrites.set(path, lifetime) + let released = false + const discard = (): void => { + if (released) { + return + } + released = true + buffer.length = 0 + bufferedChars = 0 + lifetime.readers-- + if (lifetime.readers === 0 && this.activeWrites.get(path) === lifetime) { + this.activeWrites.delete(path) + } + } // A replace owns the session's whole row set, so the old generation goes in // the same transaction as the first of the new one. Chunk two onwards must // not repeat it. @@ -232,11 +262,9 @@ export class SessionSearchIndexWriter { // transaction it already knows will roll back, once per remaining message. let fenced = false - // Why a counter and not the cursor alone: on a path this index never wrote, - // `expected` and the absent row are both undefined, so the cursor compare - // reads a removal as no change and the write recreates the source. + // A missing cursor cannot distinguish a first read from its removed source. const current = (): boolean => { - if ((this.removals.get(path) ?? 0) !== removalsAtStart) { + if (lifetime.removed) { return false } const row = this.cursor(path) @@ -320,7 +348,8 @@ export class SessionSearchIndexWriter { return { add: (message) => { - if (fenced) { + if (released || fenced || this.closed) { + discard() return } hash = foldContentHash(hash, [message]) @@ -341,13 +370,19 @@ export class SessionSearchIndexWriter { const named = identity?.() ?? null if (named && !write(null, named)) { fenced = true - buffer.length = 0 - bufferedChars = 0 + discard() return } } }, - commit: (outcome) => !fenced && write(outcome, null) + commit: (outcome) => { + try { + return !released && !fenced && !this.closed && write(outcome, null) + } finally { + discard() + } + }, + discard } } diff --git a/src/main/ai-vault-search/session-search-store.ts b/src/main/ai-vault-search/session-search-store.ts index 73349970b1b..73da2c2db6f 100644 --- a/src/main/ai-vault-search/session-search-store.ts +++ b/src/main/ai-vault-search/session-search-store.ts @@ -361,6 +361,7 @@ export class SessionSearchStore { return } this.closed = true + this.writer.close() this.db.close() } } diff --git a/src/main/ai-vault-search/session-search-write-lifetime.test.ts b/src/main/ai-vault-search/session-search-write-lifetime.test.ts new file mode 100644 index 00000000000..ef0007b58ce --- /dev/null +++ b/src/main/ai-vault-search/session-search-write-lifetime.test.ts @@ -0,0 +1,209 @@ +import { afterEach, beforeEach, expect, it, vi } from 'vitest' +import { join } from 'node:path' +import { TranscriptMessageChannel } from '../ai-vault/session-transcript-channel' +import { registerSessionSearchIndexConsumer } from './session-search-index-consumer' +import { SessionSearchIndexWriter } from './session-search-index-writer' +import { + openSessionSearchIndexFile, + syntheticCandidate, + syntheticSession, + type SessionSearchIndexFile +} from './session-search-index-test-fixture' +import { SessionSearchStore } from './session-search-store' + +let index: SessionSearchIndexFile +let store: SessionSearchStore +let writer: SessionSearchIndexWriter +let unregister: () => void +const message = { role: 'user' as const, text: 'needle', timestamp: null } +const outcome = { session: syntheticSession(), byteOffset: 100, incomplete: false } + +function trackedPaths(): number { + return writer['activeWrites'].size +} + +function openRead(named = true): TranscriptMessageChannel { + const channel = new TranscriptMessageChannel() + channel.beginRead({ + candidate: syntheticCandidate(), + mode: 'replace', + previousByteOffset: 0, + identity: named ? () => syntheticSession() : undefined + }) + return channel +} + +function failInsert(): void { + const prepare = index.db.prepare.bind(index.db) + vi.spyOn(index.db, 'prepare').mockImplementation((sql) => { + if (sql.startsWith('INSERT INTO sessions')) { + throw new Error('Synthetic insert failure') + } + return prepare(sql) + }) +} + +beforeEach(async () => { + index = await openSessionSearchIndexFile('ss-write-lifetime') + store = new SessionSearchStore(index.path) + writer = new SessionSearchIndexWriter(index.db, 1) + vi.spyOn(store, 'beginWrite').mockImplementation((...args) => writer.beginWrite(...args)) + unregister = registerSessionSearchIndexConsumer(store) +}) + +afterEach(async () => { + unregister() + writer.close() + vi.restoreAllMocks() + store.close() + await index.close() +}) + +it('retains no path metadata for repeated deletions without active writes', () => { + for (let index = 0; index < 1000; index++) { + writer.removeFile(join('synthetic', `retired-${index}.jsonl`)) + } + expect(trackedPaths()).toBe(0) +}) + +it('keeps the fence across intermediate chunks and releases it after final commit', () => { + const write = writer.beginWrite(syntheticCandidate(), 'replace', 0, () => syntheticSession())! + write.add(message) + expect(index.db.prepare('SELECT count(*) AS n FROM messages').get()).toMatchObject({ n: 1 }) + expect(trackedPaths()).toBe(1) + expect(write.commit(outcome)).toBe(true) + expect(trackedPaths()).toBe(0) + write.discard() + write.discard() + expect(write.commit(outcome)).toBe(false) + expect(trackedPaths()).toBe(0) +}) + +it('keeps concurrent reads fenced until each ends', () => { + const first = writer.beginWrite(syntheticCandidate(), 'replace', 0)! + const second = writer.beginWrite(syntheticCandidate(), 'replace', 0)! + first.discard() + first.discard() + expect(trackedPaths()).toBe(1) + writer.removeFile(syntheticCandidate().file.path) + second.add(message) + expect(second.commit(outcome)).toBe(false) + expect(trackedPaths()).toBe(0) + expect(index.db.prepare('SELECT count(*) AS n FROM files').get()).toMatchObject({ n: 0 }) +}) + +it('preserves the new generation when an older removed read finishes', () => { + const candidate = syntheticCandidate() + const old = writer.beginWrite(candidate, 'replace', 0)! + writer.removeFile(candidate.file.path) + expect(trackedPaths()).toBe(0) + const current = writer.beginWrite(candidate, 'replace', 0)! + old.discard() + expect(trackedPaths()).toBe(1) + writer.removeFile(candidate.file.path) + current.add(message) + expect(current.commit(outcome)).toBe(false) + expect(trackedPaths()).toBe(0) + expect(index.db.prepare('SELECT count(*) AS n FROM files').get()).toMatchObject({ n: 0 }) +}) + +it('permits a fresh read after removal while still refusing an older commit', () => { + const candidate = syntheticCandidate() + const old = writer.beginWrite(candidate, 'replace', 0)! + writer.removeFile(candidate.file.path) + const current = writer.beginWrite(candidate, 'replace', 0)! + old.add(message) + expect(old.commit(outcome)).toBe(false) + expect(trackedPaths()).toBe(1) + current.add(message) + expect(current.commit(outcome)).toBe(true) + expect(trackedPaths()).toBe(0) +}) + +it('releases a write when its final transaction throws', () => { + const write = writer.beginWrite(syntheticCandidate(), 'replace', 0)! + write.add(message) + failInsert() + expect(() => write.commit(outcome)).toThrow('Synthetic insert failure') + expect(trackedPaths()).toBe(0) + write.discard() + expect(trackedPaths()).toBe(0) +}) + +it('discards an incomplete consumer read without publishing its buffer', () => { + const channel = openRead(false) + channel.push(message) + expect(trackedPaths()).toBe(1) + channel.finishRead({ session: null, byteOffset: 0, incomplete: true }) + expect(trackedPaths()).toBe(0) + expect(index.db.prepare('SELECT count(*) AS n FROM messages').get()).toMatchObject({ n: 0 }) + expect(index.db.prepare('SELECT state FROM files').get()).toMatchObject({ state: 'failed' }) +}) + +it.each([false, true])( + 'keeps removed content absent when a consumer finishes, chunked: %s', + (chunked) => { + const channel = openRead(chunked) + channel.push(message) + writer.removeFile(syntheticCandidate().file.path) + expect(index.db.prepare('SELECT count(*) AS n FROM files').get()).toMatchObject({ n: 0 }) + channel.finishRead(outcome) + expect(trackedPaths()).toBe(0) + expect(index.db.prepare('SELECT count(*) AS n FROM sessions').get()).toMatchObject({ n: 0 }) + expect(index.db.prepare('SELECT count(*) AS n FROM messages').get()).toMatchObject({ n: 0 }) + // Existing retry bookkeeping may recreate a failed file row, never its searchable content. + expect(index.db.prepare('SELECT state FROM files').get()).toMatchObject({ state: 'failed' }) + } +) + +it.each([false, true])( + 'releases a failed consumer even when the reporter throws: %s', + (reporterThrows) => { + vi.spyOn(store, 'reportWriteFailure').mockImplementation(() => { + if (reporterThrows) { + throw new Error('Synthetic reporter failure') + } + }) + const channel = openRead() + failInsert() + expect(() => channel.push(message)).not.toThrow() + expect(channel.active).toBe(!reporterThrows) + expect(trackedPaths()).toBe(0) + channel.finishRead(outcome) + expect(trackedPaths()).toBe(0) + } +) + +it('discards after a finish failure even if the error reporter throws', () => { + const channel = new TranscriptMessageChannel() + channel.beginRead({ candidate: syntheticCandidate(), mode: 'replace', previousByteOffset: 0 }) + channel.push(message) + vi.spyOn(store, 'reportWriteFailure').mockImplementation(() => { + throw new Error('Synthetic reporter failure') + }) + failInsert() + expect(() => channel.finishRead(outcome)).not.toThrow() + expect(channel.active).toBe(false) + expect(trackedPaths()).toBe(0) +}) + +it('invalidates every write on close and refuses later writes', () => { + const write = writer.beginWrite(syntheticCandidate(), 'replace', 0)! + write.add(message) + writer.close() + writer.close() + expect(trackedPaths()).toBe(0) + expect(() => write.add(message)).not.toThrow() + expect(write.commit(outcome)).toBe(false) + expect(writer.beginWrite(syntheticCandidate(), 'replace', 0)).toBeNull() + expect(index.db.prepare('SELECT count(*) AS n FROM files').get()).toMatchObject({ n: 0 }) +}) + +it('closes the owned writer before closing the store database', () => { + vi.mocked(store.beginWrite).mockRestore() + const write = store.beginWrite(syntheticCandidate(), 'replace', 0)! + write.add(message) + store.close() + expect(() => write.add(message)).not.toThrow() + expect(write.commit(outcome)).toBe(false) +}) From 54e11473a6d5ca53e7b6e1eabdcf99e28af5c1f9 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:27:27 -0700 Subject: [PATCH 065/168] fix(browser): fence late registration replies to their guest owner (#21012) Co-authored-by: m4air --- .../README.md | 30 ++ .../fix.patch | 126 ++++++ .../reproduce.mjs | 153 ++++++++ .../results.json | 58 +++ .../host-guest/browser-page-guest-recovery.ts | 2 + ...rowser-page-registration-ownership.test.ts | 361 ++++++++++++++++++ .../browser-page-webview-guest-session.ts | 44 ++- 7 files changed, 766 insertions(+), 8 deletions(-) create mode 100644 docs/audits/browser-registration-reply-retention/README.md create mode 100644 docs/audits/browser-registration-reply-retention/fix.patch create mode 100644 docs/audits/browser-registration-reply-retention/reproduce.mjs create mode 100644 docs/audits/browser-registration-reply-retention/results.json create mode 100644 src/renderer/src/components/browser-pane/host-guest/browser-page-registration-ownership.test.ts diff --git a/docs/audits/browser-registration-reply-retention/README.md b/docs/audits/browser-registration-reply-retention/README.md new file mode 100644 index 00000000000..413fa8ed270 --- /dev/null +++ b/docs/audits/browser-registration-reply-retention/README.md @@ -0,0 +1,30 @@ +# Late browser registration replies restore retired renderer state + +`createBrowserPageWebviewGuestSession` awaited `registerGuest` IPC and then wrote the returned guest ID into the renderer's persistent `registeredWebContentsIds` map. An explicit close could remove the webview and map entry before that reply arrived; a delayed success restored the retired entry. An older reply could also overwrite the ID of a replacement guest. Its follow-on callbacks could synchronize an obsolete annotation bridge or mutate recovery state after the listener session was disposed. Separately, recovery validation could issue repair IPC after its initial registration query outlived that owner. + +The fix checks the existing recovery disposal state, current listener ref, persistent registry identity, and captured WebContents ID before accepting a reply or running those continuations. It makes no new registry and sends no late unregister IPC. A current hidden guest still accepts successful registration. When a persistent guest remounts, the new session's existing `validateAfterResume` path retries registration if the old reply was ignored. Current unsuccessful replies and current repair retain their prior behavior. + +## Reproduce + +With dependencies already installed: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node docs/audits/browser-registration-reply-retention/reproduce.mjs +``` + +This runs the actual renderer session, recovery controller, and persistent guest registry against headless DOM fixtures and deferred IPC replies. The baseline reverses only the included production patch in memory. A temporary observer records map/callback counts after all replies settle. The script uses the shared process launcher, 512 MiB workers, a 60-second deadline, and temporary files removed in `finally`. No Orca window, native guest, or remote host is launched. + +| After 1,000 explicit guest closes and delayed successful replies | Before | Fixed | +| ---------------------------------------------------------------- | -----: | ----: | +| Live webviews | 0 | 0 | +| Retained registration entries | 1,000 | 0 | +| Late annotation synchronizations | 1,000 | 0 | +| Unregister calls | 1,000 | 1,000 | + +The baseline fails ten tests and passes six controls; fixed source passes all 16. Cases cover distinct closed IDs, replacement elements, a changed guest ID on the same element, a remount reusing the same element/ref, disposed and moved refs, registry removal before listener disposal, a throwing identity getter, hidden current guests, successful/inconclusive replies, and late versus current repair. The repair-completion case verifies that an old success cannot clear a newer guest's recovery error. All host-guest suites also pass: 196 tests across 23 files, including recovery, viewport, registry, worktree retention, and paintability. The web typecheck passes. + +## Version and limits + +Targeted reads of `v1.4.198` confirm the same unconditional registration setter, post-reply callbacks, post-query repair, and close-time map deletion. This establishes a renderer retaining path in the reported version, not that #19831 or #19768 exercised it. Each retained entry is a page ID and numeric guest ID. This proof does not show a surviving native browser process or explain gigabyte-scale memory growth. The independent main-process destroyed-guest callback retention has its own fix and proof. + +The registration reply is the only production setter of `registeredWebContentsIds`; explicit close and replacement remove its key. Following callers found no second setter that could recreate this same metadata after removal. The annotation callback uses current page routing, which is why skipping a stale callback is necessary without issuing cleanup against a replacement. diff --git a/docs/audits/browser-registration-reply-retention/fix.patch b/docs/audits/browser-registration-reply-retention/fix.patch new file mode 100644 index 00000000000..42ac361b59d --- /dev/null +++ b/docs/audits/browser-registration-reply-retention/fix.patch @@ -0,0 +1,126 @@ +diff --git a/src/renderer/src/components/browser-pane/host-guest/browser-page-guest-recovery.ts b/src/renderer/src/components/browser-pane/host-guest/browser-page-guest-recovery.ts +index 45f3b354b5..b6e30917d1 100644 +--- a/src/renderer/src/components/browser-pane/host-guest/browser-page-guest-recovery.ts ++++ b/src/renderer/src/components/browser-pane/host-guest/browser-page-guest-recovery.ts +@@ -21,6 +21,7 @@ type BrowserPageGuestRecoveryOptions = { + export type BrowserPageGuestRecovery = { + confirmRegistration: () => void + dispose: () => void ++ isDisposed: () => boolean + finish: () => boolean + recoverRenderer: () => void + retryRecovery: () => void +@@ -263,6 +264,7 @@ export function createBrowserPageGuestRecovery( + clearValidationRetry() + clearValidationTimeout() + }, ++ isDisposed: () => disposed, + finish, + recoverRenderer, + retryRecovery: () => { +diff --git a/src/renderer/src/components/browser-pane/host-guest/browser-page-webview-guest-session.ts b/src/renderer/src/components/browser-pane/host-guest/browser-page-webview-guest-session.ts +index 4623b817b2..dd5e243ba2 100644 +--- a/src/renderer/src/components/browser-pane/host-guest/browser-page-webview-guest-session.ts ++++ b/src/renderer/src/components/browser-pane/host-guest/browser-page-webview-guest-session.ts +@@ -15,7 +15,11 @@ import { + type BrowserPageGuestRecovery + } from './browser-page-guest-recovery' + import { browserPageZoomLevelToPercent, setBrowserPageZoomLevel } from './browser-page-zoom' +-import { registeredWebContentsIds, replacePersistentWebview } from './webview-registry' ++import { ++ registeredWebContentsIds, ++ replacePersistentWebview, ++ webviewRegistry ++} from './webview-registry' + import { browserPageExists } from '../describe-page/browser-page-load-error' + import type { + BrowserPageRecoveryNavigationValidation, +@@ -80,11 +84,21 @@ export function createBrowserPageWebviewGuestSession({ + webContentsId: number + promise: Promise + } | null = null +- const registerGuest = (): Promise => { +- let webContentsId: number ++ const readWebContentsId = (): number | null => { + try { +- webContentsId = webview.getWebContentsId() ++ return webview.getWebContentsId() + } catch { ++ return null ++ } ++ } ++ const ownsGuest = (webContentsId: number | null): boolean => ++ webContentsId !== null && ++ !guestRecovery.isDisposed() && ++ webviewRef.current === webview && ++ webviewRegistry.get(browserTabId) === webview && ++ readWebContentsId() === webContentsId ++ const registerGuest = (webContentsId: number | null): Promise => { ++ if (webContentsId === null || !ownsGuest(webContentsId)) { + return Promise.resolve(null) + } + if (registrationInFlight?.webContentsId === webContentsId) { +@@ -99,6 +113,9 @@ export function createBrowserPageWebviewGuestSession({ + webContentsId + }) + .then((registered) => { ++ if (!ownsGuest(webContentsId)) { ++ return null ++ } + if (registered) { + registeredWebContentsIds.set(browserTabId, webContentsId) + return true +@@ -146,22 +163,26 @@ export function createBrowserPageWebviewGuestSession({ + return null + } + if (registeredWebContentsIds.get(browserTabId) !== webContentsId) { +- return registerGuest() ++ return registerGuest(webContentsId) + } + const registered = await window.api.browser.isGuestRegistered({ + browserPageId: browserTabId, + webContentsId + }) ++ if (!ownsGuest(webContentsId)) { ++ return null ++ } + if (registered) { + return true + } +- return window.api.browser.repairGuestRegistration({ ++ const repaired = await window.api.browser.repairGuestRegistration({ + browserPageId: browserTabId, + workspaceId, + worktreeId, + sessionProfileId, + webContentsId + }) ++ return ownsGuest(webContentsId) ? repaired : null + }, + replaceGuest: () => replacePersistentWebview(browserTabId), + onReplacementReady: () => setGuestRecoveryGeneration((generation) => generation + 1), +@@ -184,7 +205,11 @@ export function createBrowserPageWebviewGuestSession({ + + const handleDidAttach = (): void => { + // Why: register at attach since cert failures can precede dom-ready; the dom-ready path stays an idempotent fallback. +- void registerGuest().then((registered) => { ++ const webContentsId = readWebContentsId() ++ void registerGuest(webContentsId).then((registered) => { ++ if (!ownsGuest(webContentsId)) { ++ return ++ } + if (registered === true) { + guestRecovery.confirmRegistration() + } +@@ -207,7 +232,10 @@ export function createBrowserPageWebviewGuestSession({ + const queuedAnnotationViewportBridgeSync = + liveWebContentsId === null || registeredWebContentsIds.get(browserTabId) !== liveWebContentsId + if (queuedAnnotationViewportBridgeSync) { +- void registerGuest().then((registered) => { ++ void registerGuest(liveWebContentsId).then((registered) => { ++ if (!ownsGuest(liveWebContentsId)) { ++ return ++ } + const completedRecovery = guestRecovery.finish() + if (registered === true) { + guestRecovery.confirmRegistration() diff --git a/docs/audits/browser-registration-reply-retention/reproduce.mjs b/docs/audits/browser-registration-reply-retention/reproduce.mjs new file mode 100644 index 00000000000..a851be61f91 --- /dev/null +++ b/docs/audits/browser-registration-reply-retention/reproduce.mjs @@ -0,0 +1,153 @@ +import { createHash } from 'node:crypto' +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { createRequire } from 'node:module' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import { applyPatch, parsePatch, reversePatch } from 'diff' +import { build } from 'esbuild' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1.') +} + +const root = fileURLToPath(new URL('../../../', import.meta.url)) +const patch = await readFile(new URL('./fix.patch', import.meta.url), 'utf8') +const beforeSources = {} +const sourceHashes = {} +const sha256 = (source) => createHash('sha256').update(source).digest('hex') +for (const parsed of parsePatch(patch)) { + const path = parsed.newFileName.replace(/^b\//, '') + const absolute = resolve(root, path) + const current = await readFile(absolute, 'utf8') + const before = applyPatch(current, reversePatch(parsed)) + if (before === false) { + throw new Error(`Source changed; review the proof patch: ${path}`) + } + beforeSources[absolute.replaceAll('\\', '/')] = before + sourceHashes[path] = { before: sha256(before), after: sha256(current) } +} + +const testPath = + 'src/renderer/src/components/browser-pane/host-guest/browser-page-registration-ownership.test.ts' +const test = await readFile(resolve(root, testPath), 'utf8') +const countAssertion = ' expect(webviewRegistry.size).toBe(0)\n' +if (test.split(countAssertion).length !== 2) { + throw new Error('Expected exactly one closed-guest count assertion; review the observer.') +} +const observedTest = `import { writeFileSync } from 'node:fs'\n${test.replace( + countAssertion, + ` writeFileSync(process.env.ORCA_BROWSER_REGISTRATION_COUNTS_PATH, JSON.stringify({ liveWebviews: webviewRegistry.size, registeredGuestIds: registeredWebContentsIds.size, lateAnnotationSyncs: sessions.reduce((count, page) => count + page.sync.mock.calls.length, 0), unregisterCalls: unregister.mock.calls.length }))\n${countAssertion}` +)}` +sourceHashes[testPath] = { current: sha256(test), observed: sha256(observedTest) } +const scratch = await mkdtemp(join(tmpdir(), 'orca-browser-registration-reply-')) +const require = createRequire(import.meta.url) +let runnerModuleId +try { + const runnerPath = join(scratch, 'run-process.cjs') + await build({ + absWorkingDir: root, + entryPoints: [resolve(root, 'src/shared/child-process/run-process.ts')], + outfile: runnerPath, + bundle: true, + platform: 'node', + format: 'cjs', + logLevel: 'silent' + }) + runnerModuleId = require.resolve(runnerPath) + const { runProcess } = require(runnerModuleId) + const configImport = JSON.stringify(pathToFileURL(resolve(root, 'config/vitest.config.ts')).href) + + async function run(label, productionSources) { + const config = join(scratch, `${label}.config.mjs`) + const report = join(scratch, `${label}.json`) + const countsPath = join(scratch, `${label}.counts.json`) + const sources = { + ...productionSources, + [resolve(root, testPath).replaceAll('\\', '/')]: observedTest + } + await writeFile( + config, + `import base from ${configImport}; +const sources = ${JSON.stringify(sources)}; +export default {...base, test: {...base.test, include: [${JSON.stringify(testPath)}], maxWorkers: 1}, plugins: [{ + name: 'browser-registration-reply-audit', enforce: 'pre', + transform(_code, id) { + const source = sources[id.replaceAll('\\\\', '/').split('?')[0]]; + return source === undefined ? null : {code: source, map: null}; + } +}]};\n` + ) + const result = await runProcess({ + program: process.execPath, + args: [ + resolve(root, 'node_modules/vitest/vitest.mjs'), + 'run', + '--config', + config, + '--reporter=json', + `--outputFile=${report}` + ], + cwd: root, + env: { + ...process.env, + NODE_OPTIONS: '--max-old-space-size=512', + ORCA_BROWSER_REGISTRATION_COUNTS_PATH: countsPath + }, + timeoutMs: 60_000, + maxOutputBytes: 2 * 1024 * 1024 + }) + let parsed + try { + parsed = JSON.parse(await readFile(report, 'utf8')) + } catch (error) { + throw new Error(`${label} failed: ${result.stderr || result.stdout}`, { cause: error }) + } + return { + exitCode: result.code, + passed: parsed.numPassedTests, + failed: parsed.numFailedTests, + after1000ClosedGuests: JSON.parse(await readFile(countsPath, 'utf8')), + failedCases: parsed.testResults.flatMap((suite) => + suite.assertionResults + .filter((assertion) => assertion.status === 'failed') + .map((assertion) => assertion.fullName) + ) + } + } + + const before = await run('before', beforeSources) + const after = await run('after', {}) + const passed = + before.passed === 6 && + before.failed === 10 && + after.passed === 16 && + after.failed === 0 && + before.after1000ClosedGuests.liveWebviews === 0 && + before.after1000ClosedGuests.registeredGuestIds === 1000 && + after.after1000ClosedGuests.registeredGuestIds === 0 && + after.after1000ClosedGuests.unregisterCalls === 1000 + console.log( + JSON.stringify( + { + comparison: + 'Actual renderer guest session, recovery controller and persistent guest registry with deferred IPC replies; baseline reverses only fix.patch in a temporary source transform.', + provenance: { node: process.version, platform: process.platform, arch: process.arch }, + sourceHashes, + before, + after, + passed + }, + null, + 2 + ) + ) + if (!passed) { + process.exitCode = 1 + } +} finally { + if (runnerModuleId) { + delete require.cache[runnerModuleId] + } + await rm(scratch, { recursive: true, force: true }) +} diff --git a/docs/audits/browser-registration-reply-retention/results.json b/docs/audits/browser-registration-reply-retention/results.json new file mode 100644 index 00000000000..f3813295241 --- /dev/null +++ b/docs/audits/browser-registration-reply-retention/results.json @@ -0,0 +1,58 @@ +{ + "comparison": "Actual renderer guest session, recovery controller and persistent guest registry with deferred IPC replies; baseline reverses only fix.patch in a temporary source transform.", + "provenance": { + "node": "v26.6.0", + "platform": "darwin", + "arch": "arm64" + }, + "sourceHashes": { + "src/renderer/src/components/browser-pane/host-guest/browser-page-guest-recovery.ts": { + "before": "044280571771c4e90d07f5f5426878539f2b1729d9a4482c59d8fd236e7d4177", + "after": "2efdea1c4223b2f4114548a7f6ec4b576e1c6bde7819a07dc096d5167c2ba42c" + }, + "src/renderer/src/components/browser-pane/host-guest/browser-page-webview-guest-session.ts": { + "before": "2fe17f0fe4f8ef6ca7cff7ca5731d9d725dbf4b5e7944264e30f12241317fc6d", + "after": "a78cc98873e93834bf07b47bbf5d92da888dfbccef06551aa6ac3c8f6e9f29f8" + }, + "src/renderer/src/components/browser-pane/host-guest/browser-page-registration-ownership.test.ts": { + "current": "992b4730cd5546720b8b52366955386ad900d057f95c13beb6b92e5369bf5c1e", + "observed": "ebc38ebb195295170e8c355ecda539f3f2e75bb80548dbf4e97a17a7cd88c661" + } + }, + "before": { + "exitCode": 1, + "passed": 6, + "failed": 10, + "after1000ClosedGuests": { + "liveWebviews": 0, + "registeredGuestIds": 1000, + "lateAnnotationSyncs": 1000, + "unregisterCalls": 1000 + }, + "failedCases": [ + "renderer registration completion ownership does not restore 1000 closed IDs from delayed successful replies", + "renderer registration completion ownership keeps the replacement ID after an older reply arrives", + "renderer registration completion ownership keeps a new ID when the same DOM webview swaps its guest", + "renderer registration completion ownership a new session retries a disposed session registration on the same persistent guest and ref", + "renderer registration completion ownership a disposed listener cannot act after the same guest and ref are reused", + "renderer registration completion ownership does not restore a registry-removed guest before listener disposal runs", + "renderer registration completion ownership does not restore metadata when the current listener ref has moved", + "renderer registration completion ownership ignores a reply after reading the guest identity starts throwing", + "renderer registration completion ownership does not issue repair after a pending validation outlives its owner", + "renderer registration completion ownership does not clear a newer guest recovery error from a pending old repair reply" + ] + }, + "after": { + "exitCode": 0, + "passed": 16, + "failed": 0, + "after1000ClosedGuests": { + "liveWebviews": 0, + "registeredGuestIds": 0, + "lateAnnotationSyncs": 0, + "unregisterCalls": 1000 + }, + "failedCases": [] + }, + "passed": true +} diff --git a/src/renderer/src/components/browser-pane/host-guest/browser-page-guest-recovery.ts b/src/renderer/src/components/browser-pane/host-guest/browser-page-guest-recovery.ts index 45f3b354b5f..b6e30917d12 100644 --- a/src/renderer/src/components/browser-pane/host-guest/browser-page-guest-recovery.ts +++ b/src/renderer/src/components/browser-pane/host-guest/browser-page-guest-recovery.ts @@ -21,6 +21,7 @@ type BrowserPageGuestRecoveryOptions = { export type BrowserPageGuestRecovery = { confirmRegistration: () => void dispose: () => void + isDisposed: () => boolean finish: () => boolean recoverRenderer: () => void retryRecovery: () => void @@ -263,6 +264,7 @@ export function createBrowserPageGuestRecovery( clearValidationRetry() clearValidationTimeout() }, + isDisposed: () => disposed, finish, recoverRenderer, retryRecovery: () => { diff --git a/src/renderer/src/components/browser-pane/host-guest/browser-page-registration-ownership.test.ts b/src/renderer/src/components/browser-pane/host-guest/browser-page-registration-ownership.test.ts new file mode 100644 index 00000000000..22ef6137de8 --- /dev/null +++ b/src/renderer/src/components/browser-pane/host-guest/browser-page-registration-ownership.test.ts @@ -0,0 +1,361 @@ +// @vitest-environment happy-dom +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { BrowserLoadError } from '../../../../../shared/browser-workspace-types' +import { + createBrowserPageWebviewGuestSession, + type BrowserPageWebviewGuestSession +} from './browser-page-webview-guest-session' +import { + destroyPersistentWebview, + registerPersistentWebview, + registeredWebContentsIds, + webviewRegistry +} from './webview-registry' + +vi.mock('../describe-page/browser-page-load-error', () => ({ browserPageExists: () => true })) + +function deferred() { + let resolve!: (value: T) => void + let reject!: (error: Error) => void + const promise = new Promise((resolvePromise, rejectPromise) => { + resolve = resolvePromise + reject = rejectPromise + }) + return { resolve, reject, promise } +} +const replies: ReturnType>[] = [] +const registrations = vi.fn(() => { + const reply = deferred() + replies.push(reply) + return reply.promise +}) +const isRegistered = vi.fn(async () => true) +const repair = vi.fn(async () => true) +const unregister = vi.fn(async () => true) +type RegistrationTestPage = { + id: string + webview: Electron.WebviewTag + webviewRef: { current: Electron.WebviewTag | null } + session: BrowserPageWebviewGuestSession + sync: ReturnType + update: ReturnType + pending: { current: boolean } + paintable: { current: boolean } + loadFailure: { current: BrowserLoadError | null } + setId: (next: number) => void +} +const sessions: RegistrationTestPage[] = [] + +function createWebview(): Electron.WebviewTag { + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: The fixture installs the Electron webview methods exercised by guest registration and teardown on this DOM element. + return document.createElement('webview') as Electron.WebviewTag +} + +function createSession( + id: string, + guestId: number, + previous?: RegistrationTestPage +): RegistrationTestPage { + const webview = previous?.webview ?? createWebview() + let liveGuestId = guestId + webview.getWebContentsId = () => liveGuestId + webview.getZoomLevel = () => 0 + webview.setZoomLevel = vi.fn() + if (!previous) { + document.body.appendChild(webview) + registerPersistentWebview(id, webview) + } + const ref = (current: T) => ({ current }) + const webviewRef = previous?.webviewRef ?? ref(webview) + webviewRef.current = webview + const sync = vi.fn() + const update = vi.fn() + const pending = ref(false) + const paintable = ref(true) + const loadFailure = ref(null) + const session = createBrowserPageWebviewGuestSession({ + webview, + browserTabId: id, + workspaceId: 'browser-1', + worktreeId: 'wt-1', + sessionProfileId: null, + webviewRef, + isPaintableRef: paintable, + guestRecoveryPendingRef: pending, + browserTabUrlRef: ref('https://example.test'), + addressBarValueRef: ref('https://example.test'), + activeLoadFailureRef: loadFailure, + recoveryNavigationValidationRef: ref(null), + keepAddressBarFocusRef: ref(false), + paneZoomLevelRef: ref(0), + viewportPresetIdRef: ref(null), + onUpdatePageStateRef: ref(update), + setGuestRecoveryGeneration: vi.fn(), + setBrowserZoomPercent: vi.fn(), + focusAddressBarNow: () => false, + syncNavigationState: vi.fn(), + syncBrowserAnnotationViewportBridge: sync + }) + const result = { + id, + webview, + webviewRef, + session, + sync, + update, + pending, + paintable, + loadFailure, + setId: (next: number) => { + liveGuestId = next + } + } + sessions.push(result) + return result +} + +async function flush() { + await new Promise((resolve) => window.setTimeout(resolve, 0)) +} + +beforeEach(() => { + registrations.mockClear() + isRegistered.mockReset().mockResolvedValue(true) + repair.mockReset().mockResolvedValue(true) + unregister.mockClear() + Object.defineProperty(window, 'api', { + configurable: true, + value: { + browser: { + registerGuest: registrations, + unregisterGuest: unregister, + isGuestRegistered: isRegistered, + repairGuestRegistration: repair, + setViewportOverride: vi.fn(async () => true) + } + } + }) +}) + +afterEach(async () => { + for (const reply of replies.splice(0)) { + reply.resolve(false) + } + for (const page of sessions.splice(0)) { + page.session.guestRecovery.dispose() + page.webviewRef.current = null + await destroyPersistentWebview(page.id) + page.webview.remove() + } + registeredWebContentsIds.clear() +}) + +describe('renderer registration completion ownership', () => { + it('does not restore 1000 closed IDs from delayed successful replies', async () => { + for (let i = 0; i < 1000; i++) { + const page = createSession(`closed-${i}`, i + 1) + page.session.handleDidAttach() + page.session.guestRecovery.dispose() + page.webviewRef.current = null + await destroyPersistentWebview(page.id) + } + for (const reply of replies) { + reply.resolve(true) + } + await flush() + expect(webviewRegistry.size).toBe(0) + expect(registeredWebContentsIds.size).toBe(0) + expect(sessions.reduce((count, page) => count + page.sync.mock.calls.length, 0)).toBe(0) + expect(unregister).toHaveBeenCalledTimes(1000) + }) + + it('keeps the replacement ID after an older reply arrives', async () => { + const old = createSession('page', 1) + old.session.handleDidAttach() + old.session.guestRecovery.dispose() + await destroyPersistentWebview('page') + const replacement = createSession('page', 2) + replacement.session.handleDidAttach() + replies[1].resolve(true) + await flush() + replies[0].resolve(true) + await flush() + expect(registeredWebContentsIds.get('page')).toBe(2) + expect(old.sync).not.toHaveBeenCalled() + expect(replacement.sync).toHaveBeenCalledOnce() + expect(unregister).toHaveBeenCalledOnce() + }) + + it('keeps a new ID when the same DOM webview swaps its guest', async () => { + const page = createSession('page', 1) + page.session.handleDidAttach() + page.setId(2) + page.session.handleDidAttach() + replies[1].resolve(true) + await flush() + replies[0].resolve(true) + await flush() + expect(registeredWebContentsIds.get('page')).toBe(2) + expect(page.sync).toHaveBeenCalledOnce() + expect(unregister).not.toHaveBeenCalled() + }) + + it('a new session retries a disposed session registration on the same persistent guest and ref', async () => { + const old = createSession('page', 1) + old.session.handleDidAttach() + old.session.guestRecovery.dispose() + old.webviewRef.current = null + replies[0].resolve(true) + await flush() + expect(registeredWebContentsIds.has('page')).toBe(false) + expect(webviewRegistry.get('page')).toBe(old.webview) + const replacement = createSession('page', 1, old) + replacement.session.guestRecovery.validateAfterResume() + expect(registrations).toHaveBeenCalledTimes(2) + replies[1].resolve(true) + await flush() + expect(registeredWebContentsIds.get('page')).toBe(1) + expect(old.sync).not.toHaveBeenCalled() + expect(unregister).not.toHaveBeenCalled() + }) + + it('a disposed listener cannot act after the same guest and ref are reused', async () => { + const old = createSession('page', 1) + old.session.handleDomReady() + old.session.guestRecovery.dispose() + const replacement = createSession('page', 1, old) + replacement.pending.current = true + replies[0].resolve(true) + await flush() + expect(registeredWebContentsIds.has('page')).toBe(false) + expect(old.sync).not.toHaveBeenCalled() + expect(replacement.pending.current).toBe(true) + }) + + it.each(['attach', 'ready'] as const)('keeps successful current %s replies', async (event) => { + const page = createSession('page', 1) + if (event === 'attach') { + page.session.handleDidAttach() + } else { + page.session.handleDomReady() + } + replies[0].resolve(true) + await flush() + expect(registeredWebContentsIds.get('page')).toBe(1) + expect(page.sync).toHaveBeenCalledOnce() + expect(unregister).not.toHaveBeenCalled() + }) + + it.each(['false', 'reject'] as const)( + 'preserves inconclusive current registration %s', + async (result) => { + const page = createSession('page', 1) + page.session.handleDidAttach() + if (result === 'false') { + replies[0].resolve(false) + } else { + replies[0].reject(new Error('attach race')) + } + await flush() + expect(registeredWebContentsIds.has('page')).toBe(false) + expect(page.sync).toHaveBeenCalledOnce() + expect(unregister).not.toHaveBeenCalled() + } + ) + + it('does not restore a registry-removed guest before listener disposal runs', async () => { + const page = createSession('page', 1) + page.session.handleDidAttach() + await destroyPersistentWebview('page') + replies[0].resolve(true) + await flush() + expect(registeredWebContentsIds.has('page')).toBe(false) + expect(page.sync).not.toHaveBeenCalled() + }) + + it('does not restore metadata when the current listener ref has moved', async () => { + const page = createSession('page', 1) + page.session.handleDidAttach() + page.webviewRef.current = null + replies[0].resolve(true) + await flush() + expect(registeredWebContentsIds.has('page')).toBe(false) + expect(page.sync).not.toHaveBeenCalled() + expect(webviewRegistry.get('page')).toBe(page.webview) + expect(unregister).not.toHaveBeenCalled() + }) + + it('accepts a current registration while its persistent guest is hidden', async () => { + const page = createSession('page', 1) + page.paintable.current = false + page.session.handleDidAttach() + replies[0].resolve(true) + await flush() + expect(registeredWebContentsIds.get('page')).toBe(1) + expect(page.sync).toHaveBeenCalledOnce() + expect(unregister).not.toHaveBeenCalled() + }) + + it('ignores a reply after reading the guest identity starts throwing', async () => { + const page = createSession('page', 1) + page.session.handleDidAttach() + page.webview.getWebContentsId = () => { + throw new Error('guest detached') + } + replies[0].resolve(true) + await flush() + expect(registeredWebContentsIds.has('page')).toBe(false) + expect(page.sync).not.toHaveBeenCalled() + }) + + it('does not issue repair after a pending validation outlives its owner', async () => { + const reply = deferred() + isRegistered.mockReturnValue(reply.promise) + const page = createSession('page', 1) + registeredWebContentsIds.set('page', 1) + page.session.guestRecovery.validateAfterResume() + page.session.guestRecovery.dispose() + await destroyPersistentWebview('page') + reply.resolve(false) + await flush() + expect(repair).not.toHaveBeenCalled() + expect(unregister).toHaveBeenCalledOnce() + }) + + it('still repairs an inconclusive current registration', async () => { + isRegistered.mockResolvedValue(false) + const page = createSession('page', 1) + registeredWebContentsIds.set('page', 1) + page.session.guestRecovery.validateAfterResume() + await flush() + expect(repair).toHaveBeenCalledExactlyOnceWith({ + browserPageId: 'page', + workspaceId: 'browser-1', + worktreeId: 'wt-1', + sessionProfileId: null, + webContentsId: 1 + }) + }) + + it('does not clear a newer guest recovery error from a pending old repair reply', async () => { + const reply = deferred() + repair.mockReturnValue(reply.promise) + isRegistered.mockResolvedValue(false) + const page = createSession('page', 1) + registeredWebContentsIds.set('page', 1) + page.session.guestRecovery.validateAfterResume() + await flush() + expect(repair).toHaveBeenCalledOnce() + page.setId(2) + const failure = { + code: -10_000, + description: 'Replacement guest recovery failed', + validatedUrl: 'https://example.test' + } + page.loadFailure.current = failure + reply.resolve(true) + await flush() + expect(page.loadFailure.current).toBe(failure) + expect(page.update).not.toHaveBeenCalled() + }) +}) diff --git a/src/renderer/src/components/browser-pane/host-guest/browser-page-webview-guest-session.ts b/src/renderer/src/components/browser-pane/host-guest/browser-page-webview-guest-session.ts index 4623b817b2a..dd5e243ba25 100644 --- a/src/renderer/src/components/browser-pane/host-guest/browser-page-webview-guest-session.ts +++ b/src/renderer/src/components/browser-pane/host-guest/browser-page-webview-guest-session.ts @@ -15,7 +15,11 @@ import { type BrowserPageGuestRecovery } from './browser-page-guest-recovery' import { browserPageZoomLevelToPercent, setBrowserPageZoomLevel } from './browser-page-zoom' -import { registeredWebContentsIds, replacePersistentWebview } from './webview-registry' +import { + registeredWebContentsIds, + replacePersistentWebview, + webviewRegistry +} from './webview-registry' import { browserPageExists } from '../describe-page/browser-page-load-error' import type { BrowserPageRecoveryNavigationValidation, @@ -80,11 +84,21 @@ export function createBrowserPageWebviewGuestSession({ webContentsId: number promise: Promise } | null = null - const registerGuest = (): Promise => { - let webContentsId: number + const readWebContentsId = (): number | null => { try { - webContentsId = webview.getWebContentsId() + return webview.getWebContentsId() } catch { + return null + } + } + const ownsGuest = (webContentsId: number | null): boolean => + webContentsId !== null && + !guestRecovery.isDisposed() && + webviewRef.current === webview && + webviewRegistry.get(browserTabId) === webview && + readWebContentsId() === webContentsId + const registerGuest = (webContentsId: number | null): Promise => { + if (webContentsId === null || !ownsGuest(webContentsId)) { return Promise.resolve(null) } if (registrationInFlight?.webContentsId === webContentsId) { @@ -99,6 +113,9 @@ export function createBrowserPageWebviewGuestSession({ webContentsId }) .then((registered) => { + if (!ownsGuest(webContentsId)) { + return null + } if (registered) { registeredWebContentsIds.set(browserTabId, webContentsId) return true @@ -146,22 +163,26 @@ export function createBrowserPageWebviewGuestSession({ return null } if (registeredWebContentsIds.get(browserTabId) !== webContentsId) { - return registerGuest() + return registerGuest(webContentsId) } const registered = await window.api.browser.isGuestRegistered({ browserPageId: browserTabId, webContentsId }) + if (!ownsGuest(webContentsId)) { + return null + } if (registered) { return true } - return window.api.browser.repairGuestRegistration({ + const repaired = await window.api.browser.repairGuestRegistration({ browserPageId: browserTabId, workspaceId, worktreeId, sessionProfileId, webContentsId }) + return ownsGuest(webContentsId) ? repaired : null }, replaceGuest: () => replacePersistentWebview(browserTabId), onReplacementReady: () => setGuestRecoveryGeneration((generation) => generation + 1), @@ -184,7 +205,11 @@ export function createBrowserPageWebviewGuestSession({ const handleDidAttach = (): void => { // Why: register at attach since cert failures can precede dom-ready; the dom-ready path stays an idempotent fallback. - void registerGuest().then((registered) => { + const webContentsId = readWebContentsId() + void registerGuest(webContentsId).then((registered) => { + if (!ownsGuest(webContentsId)) { + return + } if (registered === true) { guestRecovery.confirmRegistration() } @@ -207,7 +232,10 @@ export function createBrowserPageWebviewGuestSession({ const queuedAnnotationViewportBridgeSync = liveWebContentsId === null || registeredWebContentsIds.get(browserTabId) !== liveWebContentsId if (queuedAnnotationViewportBridgeSync) { - void registerGuest().then((registered) => { + void registerGuest(liveWebContentsId).then((registered) => { + if (!ownsGuest(liveWebContentsId)) { + return + } const completedRecovery = guestRecovery.finish() if (registered === true) { guestRecovery.confirmRegistration() From b4a6e2a80aea16838264837ab2d471577690499f Mon Sep 17 00:00:00 2001 From: Lucas Farias Date: Fri, 18 Sep 2026 00:27:33 -0300 Subject: [PATCH 066/168] fix(filesystem): match allowed roots across Unicode forms (#21194) * fix(filesystem): match allowed roots across Unicode forms macOS returns a path in whichever Unicode form its source held: APFS gives back what it stores (NFD), while the file picker and git (core.precomposeunicode) give back NFC. A workspace registered in one form never matched a file read in the other, so fs:readFile denied a path inside the open workspace (#21172). isDescendantOrEqual now compares byte-exactly first and retries in NFC only when that fails and both sides carry non-ASCII, leaving ASCII containment and the traversal guards untouched. * fix(filesystem): prove identity before admitting a Unicode-folded root Canonical equivalence is not identity: APFS folds both spellings onto one directory, but a byte-exact filesystem can hold them as distinct siblings, and admitting the unregistered one widened the allow-list. The NFC fold now only locates the ancestor of the target that the registered root would have to be; containment is granted only when that ancestor and the root stat to the same dev+ino. A failed stat or an ino of 0 denies. ASCII paths and roots that do not fold onto the target never reach the disk. --- ...-containment-unicode-normalization.test.ts | 203 ++++++++++++++++++ src/main/ipc/filesystem-path-containment.ts | 82 ++++++- 2 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 src/main/ipc/filesystem-path-containment-unicode-normalization.test.ts diff --git a/src/main/ipc/filesystem-path-containment-unicode-normalization.test.ts b/src/main/ipc/filesystem-path-containment-unicode-normalization.test.ts new file mode 100644 index 00000000000..f5348061ab9 --- /dev/null +++ b/src/main/ipc/filesystem-path-containment-unicode-normalization.test.ts @@ -0,0 +1,203 @@ +import { mkdir, mkdtemp, realpath, rm, symlink, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { afterEach, describe, expect, it } from 'vitest' + +import type { Store } from '../persistence' +import type { Repo } from '../../shared/repo-types' +import { PATH_ACCESS_DENIED_MESSAGE, resolveAuthorizedPath } from './filesystem-auth' +import { isDescendantOrEqual } from './filesystem-path-containment' + +/** + * Opening any file under a Korean-named workspace failed on macOS with + * "Access denied: path resolves outside allowed directories" (#21172). + * + * The file was inside the workspace the whole time. The two sides of the containment check reached + * it through different doors: the root was registered from the file picker or from git, which spell + * the name in NFC, while the path being read came back from the filesystem in NFD. Same name, same + * file on APFS, different bytes — so the guard reported an escape. + * + * "Same file" is the load-bearing half, so it is the half that gets proven: both spellings reaching + * one directory authorize the file, two distinct directories — which ext4 allows and APFS does not + * — leave the unregistered one denied. A symlink stands in for the APFS fold on a byte-exact host. + */ + +const FOLDER = '테스트프로젝트' +const NFC_FOLDER = FOLDER.normalize('NFC') +const NFD_FOLDER = FOLDER.normalize('NFD') + +const scratchDirs: string[] = [] + +async function makeScratchDir(): Promise { + // realpath first: macOS fronts the temp dir with a /var symlink of its own. + const scratch = await mkdtemp(join(await realpath(tmpdir()), 'orca-unicode-path-')) + scratchDirs.push(scratch) + return scratch +} + +function isEEXIST(error: unknown): boolean { + return error instanceof Error && 'code' in error && error.code === 'EEXIST' +} + +/** One directory, both spellings. EEXIST means the filesystem already folded them itself. */ +async function makeOneDirectoryTwoSpellings( + scratch: string +): Promise<{ onDisk: string; registered: string }> { + const onDisk = join(scratch, NFD_FOLDER) + const registered = join(scratch, NFC_FOLDER) + await mkdir(onDisk) + try { + await symlink(onDisk, registered, 'dir') + } catch (error) { + if (!isEEXIST(error)) { + throw error + } + } + return { onDisk, registered } +} + +/** Two canonically equal names as two distinct directories; null where the filesystem folds them. */ +async function makeDistinctSiblings( + scratch: string +): Promise<{ registered: string; sibling: string } | null> { + const registered = join(scratch, NFC_FOLDER) + const sibling = join(scratch, NFD_FOLDER) + await mkdir(registered) + try { + await mkdir(sibling) + } catch (error) { + if (isEEXIST(error)) { + return null + } + throw error + } + return { registered, sibling } +} + +function makeStore(repoPath: string): Store { + const repo: Repo = { + id: 'repo-1', + path: repoPath, + displayName: 'workspace', + badgeColor: '#000000', + addedAt: 1, + kind: 'git' + } + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the guard reads only these four accessors; Store is a class, so a structural double cannot satisfy it without the cast. + return { + getRepos: () => [repo], + getProjectGroups: () => [], + getFolderWorkspaces: () => [], + getSettings: () => ({}) + } as unknown as Store +} + +afterEach(async () => { + await Promise.all(scratchDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true }))) +}) + +describe('path containment across Unicode forms', () => { + it('spells the fixture two ways, or the rest of this file proves nothing', () => { + expect(NFC_FOLDER).not.toBe(NFD_FOLDER) + }) + + it('accepts a child of a root the filesystem spells the other way', async () => { + const scratch = await makeScratchDir() + const { onDisk, registered } = await makeOneDirectoryTwoSpellings(scratch) + + expect(isDescendantOrEqual(join(onDisk, 'test.txt'), registered)).toBe(true) + }) + + it('accepts the root itself under its other spelling', async () => { + const scratch = await makeScratchDir() + const { onDisk, registered } = await makeOneDirectoryTwoSpellings(scratch) + + expect(isDescendantOrEqual(onDisk, registered)).toBe(true) + }) + + it('denies a canonically equal sibling that is a distinct directory', async () => { + const scratch = await makeScratchDir() + const siblings = await makeDistinctSiblings(scratch) + if (!siblings) { + // The filesystem folds the two names, so there is no second directory to reach. + return + } + + expect( + isDescendantOrEqual(join(siblings.sibling, 'test.txt'), siblings.registered), + 'the sibling is a different directory; the user opened only the registered one' + ).toBe(false) + }) + + it('denies a fold it cannot check against the filesystem', () => { + // Neither spelling exists on disk, so identity is unproven — and unproven is denied. + expect( + isDescendantOrEqual(resolve(`/repos/${NFD_FOLDER}/test.txt`), resolve(`/repos/${NFC_FOLDER}`)) + ).toBe(false) + }) + + it('still rejects a sibling that only looks similar', () => { + // 테스트 is a prefix of 테스트프로젝트, not a canonical equivalent of it. + expect( + isDescendantOrEqual(resolve('/repos/테스트/test.txt'), resolve(`/repos/${NFC_FOLDER}`)) + ).toBe(false) + }) + + it('still rejects an escape out of a non-ASCII root', () => { + expect( + isDescendantOrEqual( + resolve(`/repos/${NFD_FOLDER}/../secrets`), + resolve(`/repos/${NFC_FOLDER}`) + ) + ).toBe(false) + }) + + it('leaves ASCII containment exactly as it was', () => { + expect(isDescendantOrEqual(resolve('/repos/app/src'), resolve('/repos/app'))).toBe(true) + expect(isDescendantOrEqual(resolve('/repos/apple'), resolve('/repos/app'))).toBe(false) + expect(isDescendantOrEqual(resolve('/repos/app'), resolve('/repos/app'))).toBe(true) + }) +}) + +describe('fs:readFile authorization for a Korean-named workspace', () => { + it('authorizes a file the filesystem spells the other way', async () => { + const scratch = await makeScratchDir() + const { onDisk, registered } = await makeOneDirectoryTwoSpellings(scratch) + const file = join(onDisk, 'test.txt') + await writeFile(file, 'hello') + + const store = makeStore(registered) + // Resolved before the assertion so a rejection lands on expect(), not on an unawaited promise. + const expected = await realpath(file) + + await expect( + resolveAuthorizedPath(file, store), + 'the file is inside the opened workspace; only its spelling differs' + ).resolves.toBe(expected) + }) + + it('denies a canonically equal sibling the user never opened', async () => { + const scratch = await makeScratchDir() + const siblings = await makeDistinctSiblings(scratch) + if (!siblings) { + return + } + const file = join(siblings.sibling, 'test.txt') + await writeFile(file, 'secret') + + const store = makeStore(siblings.registered) + + await expect(resolveAuthorizedPath(file, store)).rejects.toThrow(PATH_ACCESS_DENIED_MESSAGE) + }) + + it('still denies a file outside the workspace', async () => { + const scratch = await makeScratchDir() + await makeOneDirectoryTwoSpellings(scratch) + const outside = join(scratch, 'outside.txt') + await writeFile(outside, 'secret') + + const store = makeStore(join(scratch, NFC_FOLDER)) + + await expect(resolveAuthorizedPath(outside, store)).rejects.toThrow(PATH_ACCESS_DENIED_MESSAGE) + }) +}) diff --git a/src/main/ipc/filesystem-path-containment.ts b/src/main/ipc/filesystem-path-containment.ts index 32d1c00f0e1..c696028cbc8 100644 --- a/src/main/ipc/filesystem-path-containment.ts +++ b/src/main/ipc/filesystem-path-containment.ts @@ -1,11 +1,23 @@ -import { resolve, relative, isAbsolute, sep } from 'node:path' +import { resolve, relative, isAbsolute, sep, dirname } from 'node:path' +import { statSync } from 'node:fs' import { realpath } from 'node:fs/promises' /** * Check whether resolvedTarget is equal to or a descendant of resolvedBase. * Uses relative() so it works with both `/` (Unix) and `\` (Windows) separators. + * + * Compared byte-exactly first, then across Unicode forms — but only where both spellings prove to + * be one filesystem object. */ export function isDescendantOrEqual(resolvedTarget: string, resolvedBase: string): boolean { + if (isDescendantOrEqualExact(resolvedTarget, resolvedBase)) { + return true + } + const ancestor = foldedContainmentAncestor(resolvedTarget, resolvedBase) + return ancestor !== null && isSameFilesystemObject(ancestor, resolvedBase) +} + +function isDescendantOrEqualExact(resolvedTarget: string, resolvedBase: string): boolean { if (resolvedTarget === resolvedBase) { return true } @@ -15,6 +27,74 @@ export function isDescendantOrEqual(resolvedTarget: string, resolvedBase: string return rel !== '' && !(rel === '..' || rel.startsWith(`..${sep}`)) && !isAbsolute(rel) } +/** + * Why a loop and not a regex: `[^\u0000-\u007f]` trips no-control-regex, and this runs once per + * registered root on every filesystem IPC, where charCodeAt beats an ICU-backed scan anyway. + */ +function hasNonAscii(value: string): boolean { + for (let index = 0; index < value.length; index += 1) { + if (value.charCodeAt(index) > 0x7f) { + return true + } + } + return false +} + +/** + * The same name, spelled two ways. + * + * macOS returns a path in whichever Unicode form its source held: APFS gives back the form it + * stores — NFD for names typed into Finder — while the file picker and git (`core.precomposeunicode`) + * give back NFC. A workspace whose path contains Korean, accented or otherwise composed characters + * is registered in one form and read in the other, so byte comparison puts the file outside its own + * workspace and fs:readFile denies a path the user is looking at (#21172). ASCII paths are immune, + * which is why the guard held for so long. + * + * Canonical equivalence is not containment on its own: APFS folds both spellings onto one + * directory, ext4 keeps them as distinct siblings, and the unregistered sibling is not the root — + * equivalence includes singletons such as U+212A KELVIN SIGN folding to K. So the fold only locates + * the candidate ancestor, in the caller's spelling; isDescendantOrEqual settles identity. + * + * Walks up by component count, never by offset: NFD is longer than NFC. + */ +function foldedContainmentAncestor(resolvedTarget: string, resolvedBase: string): string | null { + // Both sides must carry non-ASCII before normalize() earns its allocation: ASCII is identical in + // every form, so a mismatch confined to it is a real one. Target first — it is the side the + // allow-list scan holds fixed while it walks every registered root. + if (!hasNonAscii(resolvedTarget) || !hasNonAscii(resolvedBase)) { + return null + } + const foldedBase = resolvedBase.normalize('NFC') + const foldedTarget = resolvedTarget.normalize('NFC') + if (!isDescendantOrEqualExact(foldedTarget, foldedBase)) { + return null + } + const descent = relative(foldedBase, foldedTarget) + let ancestor = resolvedTarget + for (let depth = descent === '' ? 0 : descent.split(sep).length; depth > 0; depth -= 1) { + ancestor = dirname(ancestor) + } + return ancestor +} + +/** + * The same directory entry, not merely the same name — the question the fold is really asking, and + * only the filesystem can answer it. + * + * Fails closed: an ancestor that cannot be stat'ed has not shown it is the registered root, and ino + * is 0 on volumes that expose none. Reached only on a containment the exact comparison refused, so + * ASCII paths and non-folding roots still touch no disk. + */ +function isSameFilesystemObject(pathA: string, pathB: string): boolean { + try { + const statA = statSync(pathA) + const statB = statSync(pathB) + return statA.ino !== 0 && statA.dev === statB.dev && statA.ino === statB.ino + } catch { + return false + } +} + /** * Node's canonical ENOENT message. Matched in full so a message that merely mentions the word — a * log line, a user's branch name — cannot be mistaken for a missing path. From 41059f65b25d9ea1f67a3ed92ca795d98450a667 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:28:52 -0700 Subject: [PATCH 067/168] fix(pty): reconcile daemon exits after synthetic notifications (#21000) Co-authored-by: m4air --- docs/audits/daemon-late-exit/README.md | 70 ++++++ docs/audits/daemon-late-exit/reproduce.mjs | 120 +++++++++ docs/audits/daemon-late-exit/results.json | 236 ++++++++++++++++++ .../ipc/pty-runtime-kill-and-exit.test.ts | 11 +- .../ipc/pty/daemon-late-exit-test-fixture.ts | 211 ++++++++++++++++ src/main/ipc/pty/daemon-late-exit.test.ts | 235 +++++++++++++++++ src/main/ipc/pty/delivery/exit.ts | 24 +- src/main/ipc/pty/delivery/wire-session.ts | 6 +- src/main/ipc/pty/ipc/renderer-kill.ts | 6 +- src/main/ipc/pty/provider/bind-listeners.ts | 11 +- src/main/ipc/pty/runtime/controller-deps.ts | 2 +- src/main/ipc/pty/runtime/kill.ts | 17 +- src/main/ipc/pty/session.ts | 9 +- 13 files changed, 927 insertions(+), 31 deletions(-) create mode 100644 docs/audits/daemon-late-exit/README.md create mode 100644 docs/audits/daemon-late-exit/reproduce.mjs create mode 100644 docs/audits/daemon-late-exit/results.json create mode 100644 src/main/ipc/pty/daemon-late-exit-test-fixture.ts create mode 100644 src/main/ipc/pty/daemon-late-exit.test.ts diff --git a/docs/audits/daemon-late-exit/README.md b/docs/audits/daemon-late-exit/README.md new file mode 100644 index 00000000000..d1896f9f158 --- /dev/null +++ b/docs/audits/daemon-late-exit/README.md @@ -0,0 +1,70 @@ +# Delayed daemon output after a synthetic exit + +A daemon stop response can arrive on its control socket before its final DATA and +EXIT events arrive on the separate stream socket. Main then emits a synthetic exit. +The delayed DATA recreates a headless model and marks the runtime PTY connected; +the old duplicate-exit check suppresses the physical EXIT before runtime cleanup. +The host has no live session, but main retains the connected record, title tracker +and headless terminal. + +## Reproduce + +From the checkout, using its installed dependencies: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node docs/audits/daemon-late-exit/reproduce.mjs /tmp/daemon-late-exit-results.json +ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/ipc/pty/daemon-late-exit.test.ts +``` + +The script uses the real daemon server, client, provider, socket pair, kill IPC +handler, listener binding, and runtime. The native subprocess boundary is a fixture +whose force-kill callback reports exit. Pausing only the stream reader makes the +independent control/stream ordering deterministic. No renderer or visible app is +launched. Temporary Vitest files are removed afterward. + +The before case moves duplicate suppression back ahead of runtime cleanup in the +loaded module only. It retains the current incarnation-aware marker representation, +which does not affect the same-incarnation race. The on-disk source stays unchanged. +The script verifies its transform boundary and records the current source hash. + +## Results + +[results.json](./results.json) contains four before/after controls: + +| Scenario | Before | After | +| -------------------------------------------------------- | ------------------------------------------------------ | -------------------------------------- | +| Kill reply overtakes queued DATA and EXIT | Connected; headless model and title tracker retained | Disconnected; both released | +| Host inventory verifies exit before queued DATA and EXIT | Connected despite an `exited` verdict; models retained | Disconnected; models released | +| Kill reply overtakes EXIT with no queued DATA | Disconnected; cause remains `stop_unverified` | Disconnected; confirmed requested stop | +| Natural DATA then EXIT | Disconnected; models released | Same | + +All eight runs receive one physical provider exit, deliver all final output to the +renderer admission boundary, send one renderer exit, and call the runtime exit +listener once. Fresh daemon inventory is empty in all cases. Additional regression +tests cover same-ID replacement, stale provider callbacks, legacy unstamped exits, +and one-time dispatch settlement with the real SQLite orchestration database. + +The fix always processes the current incarnation's provider exit in main. Duplicate +suppression applies only to the renderer notification. When the provider supplies an +incarnation, its marker names that stopped incarnation and cannot suppress a +differently stamped replacement's exit. Legacy unstamped events retain their +existing matching behavior. A matching marker restores the original stop intent +while normal exit-cause resolution still handles negative, +unconfirmed exits. No output is dropped and no wire fields or opcodes change. + +## Report correlation and limits + +The early-return listener and synthetic renderer-kill exit are present in both +`v1.4.197` (#19018) and `v1.4.192` (#17344). The reproduced `connected: true` plus +`stop_unverified` state matches #19018's reported contradiction and provides a +concrete main-process retaining path relevant to #19831. This does not prove which +ordering occurred in either user's session, explain #19018's failed subsequent +inventory/close reconciliation, or by itself prove persisted tab resurrection in +#17344. A missing `diagnostics.memory` row is not process-exit evidence; this proof +uses the owning daemon's physical exit and fresh session inventory. + +A second runtime cleanup may advance an already-retired surface's topology revision +once more. It does not republish a removed surface. Existing exit listeners and +waiters remove themselves on settlement; completed dispatches are no longer active. +The existing marker timeout remains 30 seconds. The separate asynchronous shutdown +call's ownership across its await is outside this change. diff --git a/docs/audits/daemon-late-exit/reproduce.mjs b/docs/audits/daemon-late-exit/reproduce.mjs new file mode 100644 index 00000000000..b82ca0333d8 --- /dev/null +++ b/docs/audits/daemon-late-exit/reproduce.mjs @@ -0,0 +1,120 @@ +import assert from 'node:assert/strict' +import { createHash } from 'node:crypto' +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import { startVitest } from 'vitest/node' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1.') +} +const root = fileURLToPath(new URL('../../../', import.meta.url)) +const sourcePath = join(root, 'src/main/ipc/pty/provider/bind-listeners.ts') +const source = await readFile(sourcePath, 'utf8') +const declaration = + ' const syntheticExit = session.consumeSyntheticKillExit(payload.id, payload.incarnationId)' +const notificationFence = + ' // The control reply can overtake stream data; the physical exit must retire that late output.\n' + + ' if (syntheticExit) {\n return\n }' +const restoreIntent = + ' if (syntheticExit) {\n session.runtime?.markPtyStopRequested(payload.id)\n }\n' +for (const boundary of [declaration, notificationFence, restoreIntent]) { + assert(source.includes(boundary), 'Source changed: review the baseline transform.') +} +const before = source + .replace(notificationFence, '') + .replace(restoreIntent, '') + .replace(declaration, `${declaration}\n if (syntheticExit) {\n return\n }`) +const fixturePath = join(root, 'src/main/ipc/pty/daemon-late-exit-test-fixture.ts') +const scratch = await mkdtemp(join(tmpdir(), 'orca-daemon-late-exit-proof-')) +const phases = [] +try { + for (const phase of ['before', 'after']) { + const resultPath = join(scratch, `${phase}.json`) + const testPath = join(scratch, `${phase}.test.ts`) + const configPath = join(scratch, `${phase}.config.mjs`) + await writeFile( + testPath, + ` +import { afterAll, it } from ${JSON.stringify(join(root, 'node_modules/vitest/dist/index.js'))} +import { writeFileSync } from 'node:fs' +import { startLateExitHarness } from ${JSON.stringify(fixturePath)} +const rows = [] +for (const scenario of ['queued-data', 'verified-stop', 'no-queued-data', 'natural-exit']) { + it(scenario, async () => { + const harness = await startLateExitHarness() + try { + if (scenario !== 'natural-exit') harness.pauseStream() + if (scenario !== 'no-queued-data') harness.subprocess._simulateData('final output\\r\\n') + if (scenario === 'natural-exit') harness.subprocess._simulateExit(0) + else if (scenario === 'verified-stop') { if (!await harness.stopAndWait()) throw new Error('Stop was not verified') } + else await harness.kill() + const beforeDrain = harness.runtime.captureState() + harness.resumeStream() + await harness.waitForExit() + const result = await harness.capture() + delete result.incarnationId + delete beforeDrain.incarnationId + rows.push({ scenario, beforeDrain, afterDrain: result }) + } finally { await harness.dispose() } + }) +} +afterAll(() => writeFileSync(${JSON.stringify(resultPath)}, JSON.stringify(rows))) +` + ) + await writeFile( + configPath, + ` +import base from ${JSON.stringify(pathToFileURL(join(root, 'config/vitest.config.ts')).href)} +export default { + ...base, + plugins: [{ name: 'late-exit-baseline', enforce: 'pre', transform(code, id) { + if (${JSON.stringify(phase)} === 'before' && id.replaceAll('\\\\', '/').endsWith('/src/main/ipc/pty/provider/bind-listeners.ts')) return ${JSON.stringify(before)} + } }], + test: { ...base.test, include: [${JSON.stringify(testPath)}], maxWorkers: 1, fileParallelism: false } +} +` + ) + const runner = await startVitest('test', [], { + root, + config: configPath, + watch: false, + reporters: ['dot'] + }) + assert(runner, 'Vitest did not start') + await runner.close() + const samples = JSON.parse(await readFile(resultPath, 'utf8')) + assert.equal(samples.length, 4) + for (const sample of samples) { + const leaked = + phase === 'before' && ['queued-data', 'verified-stop'].includes(sample.scenario) + assert.equal(sample.afterDrain.connected, leaked) + assert.equal(sample.afterDrain.headlessModelRetained, leaked) + assert.equal(sample.afterDrain.titleTrackerRetained, leaked) + assert.equal(sample.afterDrain.providerHasPty, false) + assert.equal(sample.afterDrain.hostInventoryCount, 0) + assert.equal(sample.afterDrain.rendererExitCount, 1) + assert.equal(sample.afterDrain.providerExitCount, 1) + assert.equal(sample.afterDrain.exitListenerCalls, 1) + assert.deepEqual( + sample.afterDrain.deliveredData, + sample.scenario === 'no-queued-data' ? [] : ['final output\r\n'] + ) + } + phases.push({ phase, samples }) + } + const results = { + sourceSha256: createHash('sha256').update(source).digest('hex'), + baselineTransform: + 'Restore duplicate suppression before provider/runtime cleanup only; keep current incarnation-fenced markers.', + phases + } + const output = `${JSON.stringify(results, null, 2)}\n` + if (process.argv[2]) { + await writeFile(resolve(process.argv[2]), output) + } + process.stdout.write(output) +} finally { + await rm(scratch, { recursive: true, force: true }) +} diff --git a/docs/audits/daemon-late-exit/results.json b/docs/audits/daemon-late-exit/results.json new file mode 100644 index 00000000000..2dfd43a0353 --- /dev/null +++ b/docs/audits/daemon-late-exit/results.json @@ -0,0 +1,236 @@ +{ + "sourceSha256": "8da1df8409b4e1555894546df9e2fd41351b4f5620340310c2373d22bb56a0b6", + "baselineTransform": "Restore duplicate suppression before provider/runtime cleanup only; keep current incarnation-fenced markers.", + "phases": [ + { + "phase": "before", + "samples": [ + { + "scenario": "queued-data", + "beforeDrain": { + "connected": false, + "exitCause": { + "kind": "unknown", + "reason": "stop_unverified" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": null + }, + "afterDrain": { + "connected": true, + "exitCause": { + "kind": "unknown", + "reason": "stop_unverified" + }, + "headlessModelRetained": true, + "titleTrackerRetained": true, + "liveness": null, + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": ["final output\r\n"], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + }, + { + "scenario": "verified-stop", + "beforeDrain": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited" + }, + "afterDrain": { + "connected": true, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": true, + "titleTrackerRetained": true, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": ["final output\r\n"], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + }, + { + "scenario": "no-queued-data", + "beforeDrain": { + "connected": false, + "exitCause": { + "kind": "unknown", + "reason": "stop_unverified" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": null + }, + "afterDrain": { + "connected": false, + "exitCause": { + "kind": "unknown", + "reason": "stop_unverified" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": null, + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + }, + { + "scenario": "natural-exit", + "beforeDrain": { + "connected": true, + "exitCause": null, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": null + }, + "afterDrain": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": ["final output\r\n"], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + } + ] + }, + { + "phase": "after", + "samples": [ + { + "scenario": "queued-data", + "beforeDrain": { + "connected": false, + "exitCause": { + "kind": "unknown", + "reason": "stop_unverified" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": null + }, + "afterDrain": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": ["final output\r\n"], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + }, + { + "scenario": "verified-stop", + "beforeDrain": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited" + }, + "afterDrain": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": ["final output\r\n"], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + }, + { + "scenario": "no-queued-data", + "beforeDrain": { + "connected": false, + "exitCause": { + "kind": "unknown", + "reason": "stop_unverified" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": null + }, + "afterDrain": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + }, + { + "scenario": "natural-exit", + "beforeDrain": { + "connected": true, + "exitCause": null, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": null + }, + "afterDrain": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": ["final output\r\n"], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + } + ] + } + ] +} diff --git a/src/main/ipc/pty-runtime-kill-and-exit.test.ts b/src/main/ipc/pty-runtime-kill-and-exit.test.ts index 2200b2b6043..05a6d67e2d4 100644 --- a/src/main/ipc/pty-runtime-kill-and-exit.test.ts +++ b/src/main/ipc/pty-runtime-kill-and-exit.test.ts @@ -294,10 +294,11 @@ describe('registerPtyHandlers', () => { [['pty:exit', { id: 'local-pty', code: 0 }]] ) }) - it('ignores a late provider exit after synthesizing kill exit', async () => { + it('reconciles a late provider exit without repeating the synthetic renderer exit', async () => { const exitListeners = new Set<(payload: { id: string; code: number }) => void>() const runtime = { setPtyController: vi.fn(), + markPtyStopRequested: vi.fn(), onPtyExit: vi.fn() } setLocalPtyProvider({ @@ -333,8 +334,12 @@ describe('registerPtyHandlers', () => { listener({ id: 'local-pty', code: 0 }) } - expect(runtime.onPtyExit).toHaveBeenCalledTimes(1) - expect(runtime.onPtyExit).toHaveBeenCalledWith('local-pty', -1, undefined) + expect(runtime.onPtyExit).toHaveBeenCalledTimes(2) + expect(runtime.onPtyExit).toHaveBeenNthCalledWith(1, 'local-pty', -1, undefined) + expect(runtime.onPtyExit).toHaveBeenNthCalledWith(2, 'local-pty', 0, undefined, { + providerExitObserved: true + }) + expect(runtime.markPtyStopRequested).toHaveBeenCalledTimes(2) expect(mainWindow.webContents.send.mock.calls.filter((call) => call[0] === 'pty:exit')).toEqual( [['pty:exit', { id: 'local-pty', code: -1 }]] ) diff --git a/src/main/ipc/pty/daemon-late-exit-test-fixture.ts b/src/main/ipc/pty/daemon-late-exit-test-fixture.ts new file mode 100644 index 00000000000..e9ca6f48111 --- /dev/null +++ b/src/main/ipc/pty/daemon-late-exit-test-fixture.ts @@ -0,0 +1,211 @@ +import type { BrowserWindow } from 'electron' +import { Socket } from 'node:net' +import { rmSync } from 'node:fs' +import { + createMockSubprocess, + startDaemonAdapterHarness, + waitFor +} from '../../daemon/daemon-pty-adapter-test-harness' +import { OrcaRuntimeService } from '../../runtime/orca-runtime' +import { setPtyHostBindings, type PtyIpcSurface } from '../pty-host-bindings' +import { consumeSyntheticKillExit, rememberSyntheticKillExit } from './delivery/exit' +import { installPtyKillIpcHandler } from './ipc/renderer-kill' +import { bindProviderListeners } from './provider/bind-listeners' +import { ptyIncarnationById, ptyOwnership } from './provider/ownership-state' +import { getLocalPtyProvider, setLocalPtyProvider } from './provider/registry' +import { unbindLocalProviderListeners } from './provider/listener-lifecycle' +import { shutdownProviderAndDetectExit } from './provider/shutdown-detect' +import { finishPtyShutdown } from './provider/liveness' +import { stopAndWaitPtyFromRuntimeController } from './runtime/kill' +import type { PtyRuntimeControllerDeps } from './runtime/controller-deps' +import { createPtyIpcSession } from './session' +import type { DaemonPtyAdapter } from '../../daemon/daemon-pty-adapter' + +const PTY_ID = 'repo::/tmp/late-exit-audit@@terminal' +const TAB_ID = '00000000-0000-4000-8000-000000000001' +const LEAF_ID = '00000000-0000-4000-8000-000000000002' + +class LateExitRuntime extends OrcaRuntimeService { + observeExit(listener: () => void): void { + this.ptyExitListenersByPtyId.set(PTY_ID, new Set([listener])) + } + + captureState() { + const pty = this.ptysById.get(PTY_ID) + return { + connected: pty?.connected, + exitCause: pty?.lastExitCause, + incarnationId: pty?.incarnationId, + headlessModelRetained: this.headlessTerminals.has(PTY_ID), + titleTrackerRetained: this.ptyTitleTrackersByPtyId.has(PTY_ID), + liveness: this.ptyLivenessVerdictByPtyId.get(PTY_ID)?.verdict.status ?? null + } + } +} + +function adapterStreamSocket(adapter: DaemonPtyAdapter): Socket { + const socket = adapter['client']['streamSocket'] + if (!(socket instanceof Socket)) { + throw new Error('Daemon stream socket missing') + } + return socket +} + +export async function startLateExitHarness() { + let subprocess = createMockSubprocess() + const harness = await startDaemonAdapterHarness(() => { + subprocess = createMockSubprocess() + return subprocess + }) + const runtime = new LateExitRuntime() + const priorProvider = getLocalPtyProvider() + const deliveredData: string[] = [] + const rendererExits: { id: string; code: number; incarnationId?: string }[] = [] + const providerExits: { id: string; code: number; incarnationId?: string }[] = [] + let exitListenerCalls = 0 + const windowStub = { isDestroyed: () => false, webContents: { send() {} } } + const session = createPtyIpcSession({ + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: provider listeners only inspect isDestroyed and webContents.send on this headless fixture. + mainWindow: windowStub as unknown as BrowserWindow, + runtime + }) + session.acceptPtyDataForRenderer = (event) => { + deliveredData.push(event.data) + } + session.sendPtyExitToRenderer = (event) => { + rendererExits.push(event) + } + session.consumeSyntheticKillExit = (id, incarnationId) => + consumeSyntheticKillExit(session, id, incarnationId) + session.rememberSyntheticKillExit = (id, incarnationId) => + rememberSyntheticKillExit(session, id, incarnationId) + session.sendModelRestoreNeededMarker = () => false + let killHandler: Parameters[1] | undefined + setLocalPtyProvider(harness.adapter) + bindProviderListeners(session) + setPtyHostBindings({ + ipc: { + handle: (channel, handler) => { + if (channel === 'pty:kill') { + killHandler = handler + } + }, + on() {}, + removeHandler() {}, + removeAllListeners() {} + } + }) + installPtyKillIpcHandler({ + runtime, + getLocalPtyProviderStartupPromise: () => undefined, + shutdownProviderAndDetectExit, + rememberSyntheticKillExit: session.rememberSyntheticKillExit, + sendPtyExitToRenderer: session.sendPtyExitToRenderer + }) + const result = await harness.adapter.spawn({ cols: 80, rows: 24, sessionId: PTY_ID }) + ptyOwnership.set(PTY_ID, null) + if (result.incarnationId) { + ptyIncarnationById.set(PTY_ID, result.incarnationId) + } + runtime.registerPty(PTY_ID, 'repo::/tmp/late-exit-audit', null, { + tabId: TAB_ID, + leafId: LEAF_ID, + incarnationId: result.incarnationId + }) + runtime.observeExit(() => { + exitListenerCalls++ + }) + harness.adapter.onExit((event) => { + providerExits.push(event) + }) + const streamSocket = adapterStreamSocket(harness.adapter) + return { + ...harness, + runtime, + session, + result, + deliveredData, + rendererExits, + providerExits, + get subprocess() { + return subprocess + }, + respawn: async () => { + harness.adapter.clearTombstone(PTY_ID) + const replacement = await harness.adapter.spawn({ cols: 80, rows: 24, sessionId: PTY_ID }) + ptyOwnership.set(PTY_ID, null) + if (replacement.incarnationId) { + ptyIncarnationById.set(PTY_ID, replacement.incarnationId) + } + runtime.registerPty(PTY_ID, 'repo::/tmp/late-exit-audit', null, { + tabId: TAB_ID, + leafId: LEAF_ID, + incarnationId: replacement.incarnationId + }) + runtime.observeExit(() => { + exitListenerCalls++ + }) + return replacement + }, + id: PTY_ID, + deliverProviderExit: (event: { id: string; code: number; incarnationId?: string }) => { + // oxlint-disable-next-line unicorn/no-useless-spread -- copy-safe: exit listeners may unsubscribe while receiving the event. + for (const listener of [...harness.adapter['exitListeners']]) { + listener(event) + } + }, + pauseStream: () => { + streamSocket.pause() + }, + resumeStream: () => { + streamSocket.resume() + }, + kill: async () => { + if (!killHandler) { + throw new Error('PTY kill handler missing') + } + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the installed renderer-kill handler ignores its Electron event argument. + await killHandler({} as never, { id: PTY_ID }) + }, + stopAndWait: () => { + const deps = { + runtime, + getLocalPtyProviderStartupPromise: () => undefined, + shutdownProviderAndDetectExit, + rememberSyntheticKillExit: session.rememberSyntheticKillExit, + sendPtyExitToRenderer: session.sendPtyExitToRenderer, + finishPtyShutdown + } + return stopAndWaitPtyFromRuntimeController( + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: exact-stop reads only these seven controller ports and optional store; unrelated spawn ports are unused. + deps as unknown as PtyRuntimeControllerDeps, + PTY_ID + ) + }, + waitForExit: () => waitFor(() => providerExits.length > 0), + capture: async () => ({ + ...runtime.captureState(), + providerHasPty: harness.adapter.hasPty(PTY_ID), + hostInventoryCount: (await harness.adapter.listProcesses()).length, + deliveredData: [...deliveredData], + rendererExitCount: rendererExits.length, + providerExitCount: providerExits.length, + exitListenerCalls + }), + dispose: async () => { + for (const pending of session.syntheticKillExitPtyIds.values()) { + clearTimeout(pending.cleanupTimer) + } + session.syntheticKillExitPtyIds.clear() + runtime.onPtyExit(PTY_ID, 0, runtime.captureState().incarnationId ?? undefined) + unbindLocalProviderListeners() + harness.adapter.dispose() + await harness.server.shutdown() + setLocalPtyProvider(priorProvider) + setPtyHostBindings({}) + ptyOwnership.delete(PTY_ID) + ptyIncarnationById.delete(PTY_ID) + rmSync(harness.dir, { recursive: true, force: true }) + } + } +} diff --git a/src/main/ipc/pty/daemon-late-exit.test.ts b/src/main/ipc/pty/daemon-late-exit.test.ts new file mode 100644 index 00000000000..7b854ce735c --- /dev/null +++ b/src/main/ipc/pty/daemon-late-exit.test.ts @@ -0,0 +1,235 @@ +import { describe, expect, it } from 'vitest' +import { startLateExitHarness } from './daemon-late-exit-test-fixture' + +const FINAL_OUTPUT = 'delayed final output\r\n' + +describe('daemon physical exit after synthetic renderer exit', () => { + it('retires delayed output after the kill control reply overtakes the stream', async () => { + const harness = await startLateExitHarness() + try { + harness.pauseStream() + harness.subprocess._simulateData(FINAL_OUTPUT) + await harness.kill() + expect(harness.runtime.captureState().connected).toBe(false) + expect(harness.providerExits).toHaveLength(0) + expect(await harness.adapter.listProcesses()).toEqual([]) + harness.resumeStream() + await harness.waitForExit() + expect(await harness.capture()).toMatchObject({ + connected: false, + headlessModelRetained: false, + titleTrackerRetained: false, + providerHasPty: false, + hostInventoryCount: 0, + deliveredData: [FINAL_OUTPUT], + rendererExitCount: 1, + providerExitCount: 1, + exitListenerCalls: 1, + exitCause: { kind: 'operator_close' } + }) + } finally { + await harness.dispose() + } + }) + + it('does not revive a process already proven exited by fresh host inventory', async () => { + const harness = await startLateExitHarness() + try { + harness.pauseStream() + harness.subprocess._simulateData(FINAL_OUTPUT) + expect(await harness.stopAndWait()).toBe(true) + expect(harness.runtime.captureState()).toMatchObject({ connected: false, liveness: 'exited' }) + harness.resumeStream() + await harness.waitForExit() + expect(await harness.capture()).toMatchObject({ + connected: false, + liveness: 'exited', + headlessModelRetained: false, + deliveredData: [FINAL_OUTPUT], + rendererExitCount: 1, + exitListenerCalls: 1, + exitCause: { kind: 'operator_close' } + }) + } finally { + await harness.dispose() + } + }) + + it('reconciles physical exit without another renderer notification when no data was queued', async () => { + const harness = await startLateExitHarness() + try { + harness.pauseStream() + await harness.kill() + harness.resumeStream() + await harness.waitForExit() + expect(await harness.capture()).toMatchObject({ + connected: false, + headlessModelRetained: false, + deliveredData: [], + rendererExitCount: 1, + providerExitCount: 1, + exitListenerCalls: 1 + }) + } finally { + await harness.dispose() + } + }) + + it('preserves ordinary stream-ordered output and natural exit', async () => { + const harness = await startLateExitHarness() + try { + harness.subprocess._simulateData(FINAL_OUTPUT) + harness.subprocess._simulateExit(0) + await harness.waitForExit() + expect(await harness.capture()).toMatchObject({ + connected: false, + headlessModelRetained: false, + deliveredData: [FINAL_OUTPUT], + rendererExitCount: 1, + providerExitCount: 1, + exitListenerCalls: 1, + exitCause: { kind: 'exited', exitCode: 0 } + }) + } finally { + await harness.dispose() + } + }) + + it('does not suppress the replacement incarnation exit with its predecessor marker', async () => { + const harness = await startLateExitHarness() + try { + harness.pauseStream() + await harness.kill() + const replacement = await harness.respawn() + expect(replacement.id).toBe(harness.id) + expect(replacement.incarnationId).not.toBe(harness.result.incarnationId) + harness.subprocess._simulateData(FINAL_OUTPUT) + harness.subprocess._simulateExit(0) + harness.resumeStream() + await harness.waitForExit() + expect(await harness.capture()).toMatchObject({ + incarnationId: replacement.incarnationId, + connected: false, + headlessModelRetained: false, + deliveredData: [FINAL_OUTPUT], + rendererExitCount: 2, + providerExitCount: 1, + exitListenerCalls: 2, + exitCause: { kind: 'exited', exitCode: 0 } + }) + } finally { + await harness.dispose() + } + }) + + it('keeps a new synthetic marker when the old incarnation exit arrives first', async () => { + const harness = await startLateExitHarness() + try { + harness.pauseStream() + await harness.kill() + const replacement = await harness.respawn() + harness.subprocess._simulateData(FINAL_OUTPUT) + await harness.kill() + expect(harness.session.syntheticKillExitPtyIds.get(harness.id)?.incarnationId).toBe( + replacement.incarnationId + ) + harness.resumeStream() + await harness.waitForExit() + expect(await harness.capture()).toMatchObject({ + incarnationId: replacement.incarnationId, + connected: false, + headlessModelRetained: false, + deliveredData: [FINAL_OUTPUT], + rendererExitCount: 2, + providerExitCount: 1, + exitListenerCalls: 2, + exitCause: { kind: 'operator_close' } + }) + expect(harness.session.syntheticKillExitPtyIds.has(harness.id)).toBe(false) + } finally { + await harness.dispose() + } + }) + + it('rejects a stale provider exit before touching replacement state or its marker', async () => { + const harness = await startLateExitHarness() + try { + harness.pauseStream() + await harness.kill() + const replacement = await harness.respawn() + harness.session.rememberSyntheticKillExit(harness.id, replacement.incarnationId) + const marker = harness.session.syntheticKillExitPtyIds.get(harness.id) + harness.deliverProviderExit({ + id: harness.id, + code: 137, + incarnationId: harness.result.incarnationId + }) + expect(harness.session.syntheticKillExitPtyIds.get(harness.id)).toBe(marker) + expect(harness.runtime.captureState()).toMatchObject({ + connected: true, + incarnationId: replacement.incarnationId + }) + expect(harness.rendererExits).toHaveLength(1) + } finally { + await harness.dispose() + } + }) + + it('matches legacy exits only to legacy synthetic markers', async () => { + const harness = await startLateExitHarness() + try { + const session = harness.session + session.rememberSyntheticKillExit(harness.id) + expect(session.consumeSyntheticKillExit(harness.id, 'new-incarnation')).toBe(false) + expect(session.syntheticKillExitPtyIds.has(harness.id)).toBe(true) + expect(session.consumeSyntheticKillExit(harness.id)).toBe(true) + session.rememberSyntheticKillExit(harness.id, 'new-incarnation') + expect(session.consumeSyntheticKillExit(harness.id)).toBe(false) + expect(session.consumeSyntheticKillExit(harness.id, 'new-incarnation')).toBe(true) + expect(session.consumeSyntheticKillExit(harness.id, 'new-incarnation')).toBe(false) + } finally { + await harness.dispose() + } + }) +}) + +describe('synthetic exit and orchestration settlement', () => { + it('settles an active dispatch and its exit listener only once', async () => { + const { OrchestrationDb } = await import('../../runtime/orchestration/db') + const { createRootDispatch } = + await import('../../runtime/orchestration/db/root-dispatch-test-fixture') + const { vi } = await import('vitest') + const harness = await startLateExitHarness() + const db = new OrchestrationDb(':memory:') + try { + harness.runtime.setOrchestrationDb(db) + const handle = 'term_late_exit' + harness.runtime.registerPreAllocatedHandleForPty(harness.id, handle) + const run = db.createRun({ + objective: 'Late exit reconciliation', + coordinatorHandle: 'term_coordinator', + coordinatorPaneKey: + '99999999-9999-4999-8999-999999999999:88888888-8888-4888-8888-888888888888' + }) + const task = db.createTask({ spec: 'Test physical exit reconciliation', runId: run.id }) + createRootDispatch(db, task.id, handle) + const failDispatch = vi.spyOn(db, 'failDispatch') + const insertMessage = vi.spyOn(db, 'insertMessage') + harness.pauseStream() + harness.subprocess._simulateData(FINAL_OUTPUT) + await harness.kill() + const settledAfterSynthetic = failDispatch.mock.calls.length + const messagesAfterSynthetic = insertMessage.mock.calls.length + expect(settledAfterSynthetic).toBe(1) + harness.resumeStream() + await harness.waitForExit() + expect(failDispatch).toHaveBeenCalledTimes(settledAfterSynthetic) + expect(insertMessage).toHaveBeenCalledTimes(messagesAfterSynthetic) + expect((await harness.capture()).exitListenerCalls).toBe(1) + expect(harness.runtime.captureState().exitCause).toEqual({ kind: 'operator_close' }) + } finally { + await harness.dispose() + db.close() + } + }) +}) diff --git a/src/main/ipc/pty/delivery/exit.ts b/src/main/ipc/pty/delivery/exit.ts index 3825be51192..aa26bb914bc 100644 --- a/src/main/ipc/pty/delivery/exit.ts +++ b/src/main/ipc/pty/delivery/exit.ts @@ -9,17 +9,21 @@ import { getRendererInFlightCharsForPty } from './accounting' import { clearFlushTimerIfIdle } from './flush' import type { PtyIpcSession } from '../session' -export function rememberSyntheticKillExit(session: PtyIpcSession, id: string): void { +export function rememberSyntheticKillExit( + session: PtyIpcSession, + id: string, + incarnationId?: string +): void { const existing = session.syntheticKillExitPtyIds.get(id) if (existing) { - clearTimeout(existing) + clearTimeout(existing.cleanupTimer) } - // Why a timed window: providers may report the real exit after kill completes; skip only that late duplicate, not a future reused id forever. + // Only the same incarnation's late exit duplicates the synthetic renderer notification. const cleanupTimer = setTimeout(() => { session.syntheticKillExitPtyIds.delete(id) }, SYNTHETIC_KILL_EXIT_DUPLICATE_WINDOW_MS) cleanupTimer.unref?.() - session.syntheticKillExitPtyIds.set(id, cleanupTimer) + session.syntheticKillExitPtyIds.set(id, { cleanupTimer, incarnationId }) } export function rememberRetiredRejectedPty(session: PtyIpcSession, id: string): void { @@ -34,12 +38,16 @@ export function rememberRetiredRejectedPty(session: PtyIpcSession, id: string): session.retiredRejectedPtyIds.set(id, cleanupTimer) } -export function consumeSyntheticKillExit(session: PtyIpcSession, id: string): boolean { - const cleanupTimer = session.syntheticKillExitPtyIds.get(id) - if (!cleanupTimer) { +export function consumeSyntheticKillExit( + session: PtyIpcSession, + id: string, + incarnationId?: string +): boolean { + const pending = session.syntheticKillExitPtyIds.get(id) + if (!pending || pending.incarnationId !== incarnationId) { return false } - clearTimeout(cleanupTimer) + clearTimeout(pending.cleanupTimer) session.syntheticKillExitPtyIds.delete(id) return true } diff --git a/src/main/ipc/pty/delivery/wire-session.ts b/src/main/ipc/pty/delivery/wire-session.ts index 5425acb9f10..6e683fbbfe0 100644 --- a/src/main/ipc/pty/delivery/wire-session.ts +++ b/src/main/ipc/pty/delivery/wire-session.ts @@ -89,9 +89,11 @@ export function wirePtyIpcSession(session: PtyIpcSession): void { session.requestSerializedBuffer = (ptyId, opts) => requestSerializedBuffer(session, ptyId, opts) session.shutdownProviderAndDetectExit = (provider, id, opts) => shutdownProviderAndDetectExit(provider, id, opts) - session.rememberSyntheticKillExit = (id) => rememberSyntheticKillExit(session, id) + session.rememberSyntheticKillExit = (id, incarnationId) => + rememberSyntheticKillExit(session, id, incarnationId) session.rememberRetiredRejectedPty = (id) => rememberRetiredRejectedPty(session, id) - session.consumeSyntheticKillExit = (id) => consumeSyntheticKillExit(session, id) + session.consumeSyntheticKillExit = (id, incarnationId) => + consumeSyntheticKillExit(session, id, incarnationId) session.syncPtyBackgroundedDelivery = (id, caller) => syncPtyBackgroundedDelivery(session, id, caller) session.resyncBackgroundedDeliveriesAfterGateReset = () => diff --git a/src/main/ipc/pty/ipc/renderer-kill.ts b/src/main/ipc/pty/ipc/renderer-kill.ts index ecdde7088f9..9d8dfa8c30b 100644 --- a/src/main/ipc/pty/ipc/renderer-kill.ts +++ b/src/main/ipc/pty/ipc/renderer-kill.ts @@ -18,7 +18,7 @@ export type PtyKillIpcDeps = { id: string, opts: { immediate?: boolean; keepHistory?: boolean; deadlineMs?: number } ) => Promise - rememberSyntheticKillExit: (id: string) => void + rememberSyntheticKillExit: (id: string, incarnationId?: string) => void sendPtyExitToRenderer: (payload: { id: string; code: number; incarnationId?: string }) => void } @@ -70,7 +70,7 @@ export function installPtyKillIpcHandler(deps: PtyKillIpcDeps): void { }) runtime?.markPtyLivenessUnverifiable?.(args.id, SSH_PROVIDER_UNREGISTERED_REASON) runtime?.onPtyExit(args.id, -1, incarnationId) - rememberSyntheticKillExit(args.id) + rememberSyntheticKillExit(args.id, incarnationId) sendPtyExitToRenderer({ id: args.id, code: -1, @@ -100,7 +100,7 @@ export function installPtyKillIpcHandler(deps: PtyKillIpcDeps): void { const incarnationId = finishPtyShutdown(args.id, connectionId, store) if (!providerExitObserved) { runtime?.onPtyExit(args.id, -1, incarnationId) - rememberSyntheticKillExit(args.id) + rememberSyntheticKillExit(args.id, incarnationId) sendPtyExitToRenderer({ id: args.id, code: -1, diff --git a/src/main/ipc/pty/provider/bind-listeners.ts b/src/main/ipc/pty/provider/bind-listeners.ts index 8febd1e3534..935d7f60432 100644 --- a/src/main/ipc/pty/provider/bind-listeners.ts +++ b/src/main/ipc/pty/provider/bind-listeners.ts @@ -87,18 +87,23 @@ export function bindProviderListeners(session: PtyIpcSession): void { if (!isCurrentPtyExit(payload)) { return } - if (session.consumeSyntheticKillExit(payload.id)) { - return - } + const syntheticExit = session.consumeSyntheticKillExit(payload.id, payload.incarnationId) if (!isLocalProvider) { clearProviderPtyState(payload.id) ptyOwnership.delete(payload.id) markClaudePtyExited(payload.id) + if (syntheticExit) { + session.runtime?.markPtyStopRequested(payload.id) + } session.runtime?.onPtyExit(payload.id, payload.code, payload.incarnationId, { providerExitObserved: true, ...(payload.cause ? { cause: payload.cause } : {}) }) } + // The control reply can overtake stream data; the physical exit must retire that late output. + if (syntheticExit) { + return + } // Why not the whole payload: the exit cause is a main-process fact for the // runtime's records; the renderer's pty:exit contract stays as it was. session.sendPtyExitToRenderer({ diff --git a/src/main/ipc/pty/runtime/controller-deps.ts b/src/main/ipc/pty/runtime/controller-deps.ts index 0d388599bc9..c6231e5ef0b 100644 --- a/src/main/ipc/pty/runtime/controller-deps.ts +++ b/src/main/ipc/pty/runtime/controller-deps.ts @@ -68,7 +68,7 @@ export type PtyRuntimeControllerDeps = { id: string, opts: { immediate?: boolean; keepHistory?: boolean; deadlineMs?: number } ) => Promise - rememberSyntheticKillExit: (id: string) => void + rememberSyntheticKillExit: (id: string, incarnationId?: string) => void rememberRetiredRejectedPty: (id: string) => void sendPtyExitToRenderer: (payload: { id: string; code: number; incarnationId?: string }) => void sendPtySpawnedToRenderer: (id: string) => void diff --git a/src/main/ipc/pty/runtime/kill.ts b/src/main/ipc/pty/runtime/kill.ts index c04950c1245..98bdc1e97ae 100644 --- a/src/main/ipc/pty/runtime/kill.ts +++ b/src/main/ipc/pty/runtime/kill.ts @@ -48,7 +48,7 @@ export function killPtyFromRuntimeController( // The relay was never asked, so the remote shell is still running. Keep the order. recordUndelivered(incarnationId) runtime?.onPtyExit(ptyId, -1, incarnationId) - rememberSyntheticKillExit(ptyId) + rememberSyntheticKillExit(ptyId, incarnationId) sendPtyExitToRenderer({ id: ptyId, code: -1, @@ -66,7 +66,7 @@ export function killPtyFromRuntimeController( const incarnationId = finishPtyShutdown(ptyId, connectionId, store) if (!providerExitObserved && !retired) { runtime?.onPtyExit(ptyId, -1, incarnationId) - rememberSyntheticKillExit(ptyId) + rememberSyntheticKillExit(ptyId, incarnationId) sendPtyExitToRenderer({ id: ptyId, code: -1, @@ -80,7 +80,7 @@ export function killPtyFromRuntimeController( const incarnationId = finishPtyShutdown(ptyId, connectionId, store) if (!retired) { runtime?.onPtyExit(ptyId, -1, incarnationId) - rememberSyntheticKillExit(ptyId) + rememberSyntheticKillExit(ptyId, incarnationId) sendPtyExitToRenderer({ id: ptyId, code: -1, @@ -154,8 +154,9 @@ export function retireRejectedPtyFromRuntimeController( if (!ptyOwnership.has(ptyId)) { return } - runtime?.onPtyExit(ptyId, -1, ptyIncarnationById.get(ptyId)) - rememberSyntheticKillExit(ptyId) + const incarnationId = ptyIncarnationById.get(ptyId) + runtime?.onPtyExit(ptyId, -1, incarnationId) + rememberSyntheticKillExit(ptyId, incarnationId) sendPtyExitToRenderer({ id: ptyId, code: -1, @@ -175,7 +176,7 @@ export function retireRejectedPtyFromRuntimeController( connectionId ??= parsedSshId?.connectionId const incarnationId = finishPtyShutdown(ptyId, connectionId, store) runtime?.onPtyExit(ptyId, 0, incarnationId) - rememberSyntheticKillExit(ptyId) + rememberSyntheticKillExit(ptyId, incarnationId) sendPtyExitToRenderer({ id: ptyId, code: 0, @@ -270,7 +271,7 @@ export async function stopAndWaitPtyFromRuntimeController( // await, but the relay lease must still be tombstoned. const incarnationId = finishPtyShutdown(ptyId, connectionId, store) runtime?.onPtyExit(ptyId, -1, incarnationId) - rememberSyntheticKillExit(ptyId) + rememberSyntheticKillExit(ptyId, incarnationId) sendPtyExitToRenderer({ id: ptyId, code: -1, @@ -325,7 +326,7 @@ export async function stopAndWaitPtyFromRuntimeController( // The owning provider's fresh inventory observed absence, so this is a // death certificate even when its exit event was missed. runtime?.onPtyExit(ptyId, 0, incarnationId) - rememberSyntheticKillExit(ptyId) + rememberSyntheticKillExit(ptyId, incarnationId) sendPtyExitToRenderer({ id: ptyId, code: 0, diff --git a/src/main/ipc/pty/session.ts b/src/main/ipc/pty/session.ts index 3570e428b56..f6a09ed0fee 100644 --- a/src/main/ipc/pty/session.ts +++ b/src/main/ipc/pty/session.ts @@ -96,7 +96,10 @@ export type PtyIpcSession = { producerFlowControl: PtyProducerFlowController sourceCreditPendingPtys: Set backgroundedDeliverySyncByPty: Map - syntheticKillExitPtyIds: Map + syntheticKillExitPtyIds: Map< + string, + { cleanupTimer: NodeJS.Timeout; incarnationId: string | undefined } + > reversibleStopOwnersByPtyId: Map retiredRejectedPtyIds: Map pendingSerializeRequests: Map< @@ -156,9 +159,9 @@ export type PtyIpcSession = { id: string, opts: { immediate?: boolean; keepHistory?: boolean; deadlineMs?: number } ) => Promise - rememberSyntheticKillExit: (id: string) => void + rememberSyntheticKillExit: (id: string, incarnationId?: string) => void rememberRetiredRejectedPty: (id: string) => void - consumeSyntheticKillExit: (id: string) => boolean + consumeSyntheticKillExit: (id: string, incarnationId?: string) => boolean syncPtyBackgroundedDelivery: (id: string, caller: string) => void resyncBackgroundedDeliveriesAfterGateReset: () => void transitionHiddenRendererPtyDeliveryState: ( From c09e8fe59a19521da861c72fb98698d269c6b2e3 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:28:55 -0700 Subject: [PATCH 068/168] fix(sessions): stop transcript catch-up after TUI owner close (#21002) Co-authored-by: m4air --- docs/audits/tui-transcript-close/README.md | 30 ++++ .../audits/tui-transcript-close/reproduce.mjs | 140 +++++++++++++++ docs/audits/tui-transcript-close/results.json | 30 ++++ ...tured-agent-session-handoff-owner-close.ts | 1 + .../structured-tui-transcript-close.test.ts | 168 ++++++++++++++++++ 5 files changed, 369 insertions(+) create mode 100644 docs/audits/tui-transcript-close/README.md create mode 100644 docs/audits/tui-transcript-close/reproduce.mjs create mode 100644 docs/audits/tui-transcript-close/results.json create mode 100644 src/main/native-chat/agent-session-wire/structured-tui-transcript-close.test.ts diff --git a/docs/audits/tui-transcript-close/README.md b/docs/audits/tui-transcript-close/README.md new file mode 100644 index 00000000000..a5df3b9553d --- /dev/null +++ b/docs/audits/tui-transcript-close/README.md @@ -0,0 +1,30 @@ +# Closed TUI sessions retain transcript watchers + +After a structured session hands off to a terminal, `StructuredTuiTranscriptCatchup` tails the provider transcript. Successful `StructuredAgentSessionHost.close` stopped the TUI owner, durably released its lease, and removed the host session, but did not stop its transcript catchup. The live watcher and catchup state, including the previously seen message IDs, remained reachable. Repeated closes of distinct sessions could accumulate these resources until host teardown. + +The fix calls the existing `stopTuiHistoryCatchup` callback after the verified terminal close and durable lease transition succeed. It removes the catchup state and unsubscribes the watcher before later journal eviction. An unverified stop or failed lease write preserves the watcher for retry. A later journal-close failure cannot undo completed watcher cleanup. The execution host retains authority; no client-side inference of remote process death or wire change is involved. + +## Reproduce + +From the repository root with existing dependencies installed: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node docs/audits/tui-transcript-close/reproduce.mjs +``` + +The proof uses the actual structured host, record store, journal, handoff coordinator, and transcript watcher against a temporary synthetic Codex transcript. Only provider process acquisition/stop is a test transport; no real shell or Orca window launches. It removes the single cleanup call in a temporary Vite transform for the baseline, then runs the same four tests against the fixed source. It uses the repository's `runProcess` and cleans temporary configurations/module files. Source hashes are recorded in `results.json`. + +| Version | Passed | Failed | +| ---------- | -----: | -----: | +| Before fix | 0 | 4 | +| With fix | 4 | 0 | + +The successful-close case observes one added watcher, proves live TUI text reaches the journal, closes the session, verifies the lease is released and session removed, and expects the watcher count to return to its original value. Before the fix it stays one higher. The remaining cases exercise unverified terminal stop/retry, failed durable transition/retry, and failed journal eviction/retry. These are four checks of one cleanup omission. + +Existing catchup tests also verify that live appends and recovery of writes made while the host was down remain intact. All seven targeted tests passed, as did the Node typecheck and direct lint. + +## Version and limits + +Named-path reads of `v1.4.198` confirm that its host close calls the same owner-close helper, that helper omits catchup cleanup, and its catchup owns the same state/watcher lifetime. The executable comparison uses current production source. This establishes a retaining path present in the reported build; it does not establish that either #19831 or #19768 exercised this handoff-and-close sequence, or measure either report's memory growth. + +The separate asynchronous acquisition race remains open: teardown calls `stopAll` before draining handoffs, while catchup setup can still be awaiting path resolution or subscription acquisition. Merely rejecting a canceled preparation is insufficient as a full teardown fix: the forward handoff's existing failure recovery may acquire a native replacement, and the handoff drain has a five-second limit. That race needs its own owner-cancellation policy and regression proof; this change covers successful close of an acquired TUI owner. diff --git a/docs/audits/tui-transcript-close/reproduce.mjs b/docs/audits/tui-transcript-close/reproduce.mjs new file mode 100644 index 00000000000..23d3bcb34a4 --- /dev/null +++ b/docs/audits/tui-transcript-close/reproduce.mjs @@ -0,0 +1,140 @@ +import { createHash } from 'node:crypto' +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { createRequire } from 'node:module' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import { build } from 'esbuild' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1.') +} + +const root = fileURLToPath(new URL('../../../', import.meta.url)) +const productionPath = + 'src/main/native-chat/agent-session-wire/structured-agent-session-handoff-owner-close.ts' +const testPath = 'src/main/native-chat/agent-session-wire/structured-tui-transcript-close.test.ts' +const absolute = resolve(root, productionPath) +const current = await readFile(absolute, 'utf8') +const cleanup = ' input.deps.stopTuiHistoryCatchup?.(input.sessionId)\n' +if (current.split(cleanup).length !== 2) { + throw new Error('Expected exactly one successful-close cleanup; review the proof transform.') +} +const baseline = current.replace(cleanup, '') +const beforeSources = { [absolute.replaceAll('\\', '/')]: baseline } +const sourceHashes = { + [productionPath]: { + before: createHash('sha256').update(baseline).digest('hex'), + after: createHash('sha256').update(current).digest('hex') + }, + [testPath]: { + current: createHash('sha256') + .update(await readFile(resolve(root, testPath))) + .digest('hex') + } +} + +const scratch = await mkdtemp(join(tmpdir(), 'orca-tui-transcript-close-')) +const require = createRequire(import.meta.url) +let runnerModuleId +try { + const runnerPath = join(scratch, 'run-process.cjs') + await build({ + absWorkingDir: root, + entryPoints: [resolve(root, 'src/shared/child-process/run-process.ts')], + outfile: runnerPath, + bundle: true, + platform: 'node', + format: 'cjs', + logLevel: 'silent' + }) + runnerModuleId = require.resolve(runnerPath) + const { runProcess } = require(runnerModuleId) + const baselineConfig = join(scratch, 'before.config.mjs') + const fixedConfig = join(scratch, 'after.config.mjs') + const includes = [testPath] + const configImport = JSON.stringify(pathToFileURL(resolve(root, 'config/vitest.config.ts')).href) + await writeFile( + baselineConfig, + `import base from ${configImport}; +const beforeSources = ${JSON.stringify(beforeSources)}; +export default {...base, test: {...base.test, include: ${JSON.stringify(includes)}}, plugins: [{ + name: 'tui-transcript-close-before-fix', enforce: 'pre', + transform(_code, id) { + const before = beforeSources[id.replaceAll('\\\\', '/').split('?')[0]]; + return before === undefined ? null : {code: before, map: null}; + } +}]};\n` + ) + + await writeFile( + fixedConfig, + `import base from ${configImport};\nexport default {...base, test: {...base.test, include: ${JSON.stringify(includes)}}};\n` + ) + + async function run(label, config) { + const report = join(scratch, `${label}.json`) + const result = await runProcess({ + program: process.execPath, + args: [ + resolve(root, 'node_modules/vitest/vitest.mjs'), + 'run', + '--config', + config, + '--reporter=json', + `--outputFile=${report}` + ], + cwd: root, + env: process.env, + timeoutMs: 90_000, + maxOutputBytes: 4 * 1024 * 1024 + }) + let parsed + try { + parsed = JSON.parse(await readFile(report, 'utf8')) + } catch (error) { + throw new Error(`${label} runner failed: ${result.stderr || result.stdout}`, { cause: error }) + } + return { + exitCode: result.code, + passed: parsed.numPassedTests, + failed: parsed.numFailedTests, + failedCases: parsed.testResults.flatMap((suite) => + suite.assertionResults + .filter((test) => test.status === 'failed') + .map((test) => test.fullName) + ) + } + } + + const before = await run('before', baselineConfig) + const after = await run('after', fixedConfig) + const passed = + before.failed === 4 && + before.passed === 0 && + before.passed + before.failed === 4 && + after.passed === 4 && + after.failed === 0 + console.log( + JSON.stringify( + { + comparison: + 'Actual structured host/store/journal/transcript watcher; baseline removes only the successful-close stop callback in a temporary Vite transform', + sourceHashes, + before, + after, + passed + }, + null, + 2 + ) + ) + if (!passed) { + process.exitCode = 1 + } +} finally { + if (runnerModuleId) { + delete require.cache[runnerModuleId] + } + await rm(scratch, { recursive: true, force: true }) +} diff --git a/docs/audits/tui-transcript-close/results.json b/docs/audits/tui-transcript-close/results.json new file mode 100644 index 00000000000..67c80b1abbe --- /dev/null +++ b/docs/audits/tui-transcript-close/results.json @@ -0,0 +1,30 @@ +{ + "comparison": "Actual structured host/store/journal/transcript watcher; baseline removes only the successful-close stop callback in a temporary Vite transform", + "sourceHashes": { + "src/main/native-chat/agent-session-wire/structured-agent-session-handoff-owner-close.ts": { + "before": "a8f6d3a932c50784e9f614276989e4ef84b85eed0df4a2e0877df28e8d4640e5", + "after": "1cb420c0c27248eea74e07b6ce84530036fbabe656c360e814f9d937a35ba0d7" + }, + "src/main/native-chat/agent-session-wire/structured-tui-transcript-close.test.ts": { + "current": "bd3109b31f01bf84566ce26d1fc2c6d2cd9c7bbb64e0d95e445a0515ed32cf9f" + } + }, + "before": { + "exitCode": 1, + "passed": 0, + "failed": 4, + "failedCases": [ + "retires the transcript watcher when a live TUI session closes", + "keeps live history when terminal stop is unverified and retires it on retry", + "keeps the watcher until the durable owner transition succeeds", + "keeps transcript cleanup complete when later journal eviction needs retry" + ] + }, + "after": { + "exitCode": 0, + "passed": 4, + "failed": 0, + "failedCases": [] + }, + "passed": true +} diff --git a/src/main/native-chat/agent-session-wire/structured-agent-session-handoff-owner-close.ts b/src/main/native-chat/agent-session-wire/structured-agent-session-handoff-owner-close.ts index 634eab51423..9eb394734eb 100644 --- a/src/main/native-chat/agent-session-wire/structured-agent-session-handoff-owner-close.ts +++ b/src/main/native-chat/agent-session-wire/structured-agent-session-handoff-owner-close.ts @@ -30,6 +30,7 @@ export async function closeRetainedTuiOwner(input: { journalSettlement: 'not-required' }) ) + input.deps.stopTuiHistoryCatchup?.(input.sessionId) input.releaseOwner(input.sessionId) return true } diff --git a/src/main/native-chat/agent-session-wire/structured-tui-transcript-close.test.ts b/src/main/native-chat/agent-session-wire/structured-tui-transcript-close.test.ts new file mode 100644 index 00000000000..fa29002c934 --- /dev/null +++ b/src/main/native-chat/agent-session-wire/structured-tui-transcript-close.test.ts @@ -0,0 +1,168 @@ +import { appendFile, mkdir, writeFile } from 'node:fs/promises' +import { join } from 'node:path' +import { beforeEach, expect, it, vi } from 'vitest' +import { getActiveNativeChatWatcherCount } from '../transcript-watcher-count' +import { + CALLER, + adapter, + hostTestState, + replaceHostTestState +} from './structured-agent-session-host-test-harness' +import { + HOST_TEST_NOW as NOW, + HOST_TEST_SESSION as SESSION, + HOST_TEST_THREAD as THREAD, + hostTestAttachParams, + hostTestOperationId +} from './structured-agent-session-host-test-data' +import { StructuredAgentSessionHost } from './structured-agent-session-host' +import { StructuredHandoffTestRequests } from './structured-agent-session-handoff-test-requests' +import type { + StructuredAgentSessionHandoffTransport, + StructuredTuiOwner +} from './structured-agent-session-handoff-types' + +let host: StructuredAgentSessionHost +let rollout: string +let watcherBaseline: number +let closeTuiOwner: ReturnType< + typeof vi.fn> +> + +function rolloutLine(message: string): string { + return `${JSON.stringify({ + type: 'event_msg', + timestamp: '2026-08-11T10:00:00.000Z', + payload: { type: 'agent_message', message } + })}\n` +} + +function tuiOwner(fence: number, spawnToken: string): StructuredTuiOwner { + return { + terminal: { handle: 'term-tui', tabId: 'tab-tui', paneKey: 'pane-tui', ptyId: 'pty-tui' }, + process: { hostId: 'local', pid: 5200, processStartTimeMs: NOW, spawnToken }, + link: { + linkId: `tui-link-${fence}`, + handle: { provider: 'codex', threadId: THREAD }, + origin: 'resumed', + mintedAtFence: fence, + observedAt: NOW + } + } +} + +async function expectTranscriptMessage(text: string): Promise { + await appendFile(rollout, rolloutLine(text)) + await vi.waitFor(() => { + const history = host.history({ sessionId: SESSION, direction: 'tail' }) + expect( + history.ok && + history.page.items.some( + (item) => + item.body.kind === 'message' && + item.body.blocks.some((block) => block.type === 'text' && block.text === text) + ) + ).toBe(true) + }) +} + +beforeEach(async () => { + const initial = hostTestState() + await initial.host.flushAllStreamedEvents() + watcherBaseline = getActiveNativeChatWatcherCount() + closeTuiOwner = vi.fn(async () => ({})) + host = new StructuredAgentSessionHost({ + ...initial.host.deps, + adapter: { ...adapter(), closeSession: vi.fn(async () => true) }, + handoffTransport: { + hostLabel: 'Test host', + launchTui: async ({ fence, spawnToken }) => tuiOwner(fence, spawnToken), + reproveTuiOwner: async ({ owner }) => owner, + recoverTuiOwner: async (record) => + tuiOwner(record.lease.runtimeFence, record.lease.reservedSpawnToken ?? 'recovered'), + stopRecoveredOwner: async () => undefined, + closeTuiOwner, + waitForTuiExit: async () => ({}), + waitForTuiIdleOrExit: async () => 'idle', + tuiStatus: () => 'idle' + } + }) + replaceHostTestState({ host, store: initial.store }) + const accountHome = join(initial.root, 'codex-home') + const sessionsDir = join(accountHome, 'sessions', '2026', '08', '11') + await mkdir(sessionsDir, { recursive: true }) + rollout = join(sessionsDir, `rollout-2026-08-11T10-00-00-${THREAD}.jsonl`) + await writeFile(rollout, rolloutLine('before handoff')) + expect( + await host.attach( + CALLER, + hostTestAttachParams(null, { accountHome: { variable: 'CODEX_HOME', path: accountHome } }) + ) + ).toMatchObject({ ok: true }) + const requests = new StructuredHandoffTestRequests( + NOW, + SESSION, + () => initial.store.getRecord(SESSION)?.lease.runtimeFence ?? 0 + ) + expect( + await host.requestHandoff( + CALLER, + requests.request('to-tui', 'now', { operationId: hostTestOperationId() }) + ) + ).toMatchObject({ ok: true }) + await host['handoffs'].drain() + expect(await host.handoffStatus(SESSION)).toMatchObject({ owner: 'tui', phase: 'idle' }) + expect(getActiveNativeChatWatcherCount()).toBe(watcherBaseline + 1) +}) + +it('retires the transcript watcher when a live TUI session closes', async () => { + await expectTranscriptMessage('while TUI live') + await host.close(SESSION) + expect(host.hasSession(SESSION)).toBe(false) + expect(hostTestState().store.getRecord(SESSION)?.lease.claimStatus).toBe('released') + expect(getActiveNativeChatWatcherCount()).toBe(watcherBaseline) + await appendFile(rollout, rolloutLine('after TUI close')) + await host.close(SESSION) + expect(closeTuiOwner).toHaveBeenCalledOnce() + expect(getActiveNativeChatWatcherCount()).toBe(watcherBaseline) +}) + +it('keeps live history when terminal stop is unverified and retires it on retry', async () => { + closeTuiOwner.mockRejectedValueOnce(new Error('terminal exit unverified')) + await expect(host.close(SESSION)).rejects.toThrow('terminal exit unverified') + expect(host.hasSession(SESSION)).toBe(true) + expect(hostTestState().store.getRecord(SESSION)?.lease.claimStatus).toBe('live') + expect(getActiveNativeChatWatcherCount()).toBe(watcherBaseline + 1) + await expectTranscriptMessage('after unverified stop') + await host.close(SESSION) + expect(closeTuiOwner).toHaveBeenCalledTimes(2) + expect(getActiveNativeChatWatcherCount()).toBe(watcherBaseline) +}) + +it('keeps the watcher until the durable owner transition succeeds', async () => { + vi.spyOn(hostTestState().store, 'transitionHandoff').mockRejectedValueOnce( + new Error('lease write failed') + ) + await expect(host.close(SESSION)).rejects.toThrow('lease write failed') + expect(host.hasSession(SESSION)).toBe(true) + expect(getActiveNativeChatWatcherCount()).toBe(watcherBaseline + 1) + await host.close(SESSION) + expect(closeTuiOwner).toHaveBeenCalledTimes(2) + expect(getActiveNativeChatWatcherCount()).toBe(watcherBaseline) +}) + +it('keeps transcript cleanup complete when later journal eviction needs retry', async () => { + const session = host['sessions'].get(SESSION) + expect(session).toBeDefined() + if (!session) { + throw new Error('TUI session missing') + } + vi.spyOn(session.journal, 'close').mockRejectedValueOnce(new Error('journal close failed')) + await expect(host.close(SESSION)).rejects.toThrow('forget-session') + expect(host.hasSession(SESSION)).toBe(true) + expect(getActiveNativeChatWatcherCount()).toBe(watcherBaseline) + await host.close(SESSION) + expect(host.hasSession(SESSION)).toBe(false) + expect(closeTuiOwner).toHaveBeenCalledOnce() + expect(getActiveNativeChatWatcherCount()).toBe(watcherBaseline) +}) From 0e935c4b0ac132b77050fa918d0598fd78f51161 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:28:58 -0700 Subject: [PATCH 069/168] fix(runtime): terminate nonblank tail scan at the first row (#21018) Co-authored-by: m4air --- .../terminal-wait-leading-blank/README.md | 23 ++ .../terminal-wait-leading-blank/reproduce.mjs | 249 ++++++++++++++++++ .../terminal-wait-leading-blank/results.json | 172 ++++++++++++ .../runtime/terminal-wait-tail-window.test.ts | 77 ++++++ src/main/runtime/terminal-wait-tail-window.ts | 3 +- 5 files changed, 523 insertions(+), 1 deletion(-) create mode 100644 docs/audits/terminal-wait-leading-blank/README.md create mode 100644 docs/audits/terminal-wait-leading-blank/reproduce.mjs create mode 100644 docs/audits/terminal-wait-leading-blank/results.json create mode 100644 src/main/runtime/terminal-wait-tail-window.test.ts diff --git a/docs/audits/terminal-wait-leading-blank/README.md b/docs/audits/terminal-wait-leading-blank/README.md new file mode 100644 index 00000000000..959fde7c9af --- /dev/null +++ b/docs/audits/terminal-wait-leading-blank/README.md @@ -0,0 +1,23 @@ +# Terminal wait tail-window termination + +`startOfLastNonBlankLines` loops indefinitely if its input begins with a newline and contains fewer nonblank rows than requested. Once the backward cursor reaches zero, JavaScript `lastIndexOf` clamps its negative start position to zero and rediscovers the same first newline. The cursor stops advancing. + +The fix ends the scan when the cursor reaches zero and returns the existing short-tail offset, zero. It changes no prompt patterns or readiness rules. The ordinary finite-window selection cases retain their previous offsets. + +## Reproduce + +```sh +ORCA_BACKGROUND_LAUNCH=1 node docs/audits/terminal-wait-leading-blank/reproduce.mjs > /tmp/orca-terminal-wait-leading-blank.json +``` + +The script bundles actual source and reverses only the two-line termination change for the baseline. Each case runs in an isolated child with a two-second deadline and 128 MiB heap limit. The parent confirms child termination; no app windows open. Five direct helper/detector cases time out before and return after. A sufficient-row control and actual headless terminal projection controls pass in both variants. Results include source hashes, platform, timing, and an explicit v1.4.198 helper comparison. + +The regression suite uses a separate child for inputs that could hang the worker. It also tests exact row offsets with intervening whitespace, trailing blanks and tails shorter than the requested window. Fifty helper/detector tests pass. + +## Production and incident limits + +The helper is byte-identical in v1.4.198. However, all inspected main/provider/renderer visible-screen projection routes pass through `visibleNonBlankTerminalLines`, and ordinary retained-tail construction removes blank rows too. The real headless producer control confirms this filtering. Calling the public detector directly with a leading newline is therefore insufficient evidence that those production routes trigger the defect. + +A clipped 300-character preview can start at a newline. The preview fallback also preserves it when passed empty retained rows; the proof records both facts. It does not establish an actual application lifecycle that combines that fallback with a live detector call. That remains unproven. + +This is a defensive termination fix found during the memory audit. The loop itself does not allocate a growing collection. No memory magnitude was measured, and it is not an attribution of #19768's main-process growth or #19831's application-scope OOM. diff --git a/docs/audits/terminal-wait-leading-blank/reproduce.mjs b/docs/audits/terminal-wait-leading-blank/reproduce.mjs new file mode 100644 index 00000000000..6ee47701120 --- /dev/null +++ b/docs/audits/terminal-wait-leading-blank/reproduce.mjs @@ -0,0 +1,249 @@ +import { createHash } from 'node:crypto' +import { mkdtemp, readFile, rm } from 'node:fs/promises' +import { createRequire } from 'node:module' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { isDeepStrictEqual } from 'node:util' +import { build } from 'esbuild' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1.') +} + +const root = fileURLToPath(new URL('../../../', import.meta.url)) +const sourcePath = 'src/main/runtime/terminal-wait-tail-window.ts' +const absoluteSource = resolve(root, sourcePath) +const current = await readFile(absoluteSource, 'utf8') +const loop = ' while (lineEnd > 0) {' +const end = ' lineEnd = lineStart - 1\n }\n return 0\n}' +if (current.split(loop).length !== 2 || current.split(end).length !== 2) { + throw new Error('Source changed; review the baseline transform.') +} +const baseline = current + .replace(loop, ' for (;;) {') + .replace(end, ' lineEnd = lineStart - 1\n }\n}') +const sha256 = (value) => createHash('sha256').update(value).digest('hex') +const supportingSources = [ + 'src/main/runtime/terminal-wait-detection.ts', + 'src/main/runtime/orca-runtime-terminal-projection.ts', + 'src/main/runtime/terminal-tail-read.ts', + 'src/main/runtime/terminal-tail-state.ts', + 'src/main/runtime/terminal-wait-tail-state.ts', + 'src/main/daemon/headless-emulator.ts', + 'src/main/runtime/terminal-wait-tail-window.test.ts' +] +const supportingSourceHashes = Object.fromEntries( + await Promise.all( + supportingSources.map(async (path) => [path, sha256(await readFile(resolve(root, path)))]) + ) +) +const scratch = await mkdtemp(join(tmpdir(), 'orca-terminal-wait-blank-')) +const require = createRequire(import.meta.url) +let runnerId + +try { + const runnerPath = join(scratch, 'run-process.cjs') + await build({ + entryPoints: [resolve(root, 'src/shared/child-process/run-process.ts')], + outfile: runnerPath, + bundle: true, + platform: 'node', + format: 'cjs', + logLevel: 'silent' + }) + runnerId = require.resolve(runnerPath) + const { runProcess } = require(runnerId) + const entry = ` + import { startOfLastNonBlankLines } from './src/main/runtime/terminal-wait-tail-window'; + import { detectTerminalWaitBlockedReason, isKnownReadyPromptPreview } from './src/main/runtime/terminal-wait-detection'; + import { HeadlessEmulator } from './src/main/daemon/headless-emulator'; + import { projectTerminalVisibleLines, projectTerminalTailLines } from './src/main/runtime/orca-runtime-terminal-projection'; + import { buildPreview } from './src/main/runtime/terminal-tail-state'; + import { buildTerminalWaitText } from './src/main/runtime/terminal-wait-tail-state'; + const input = JSON.parse(process.argv[2]); + async function main() { + process.stdout.write(JSON.stringify({ phase: 'entered', mode: input.mode }) + '\\n'); + let value; + if (input.mode === 'window') value = startOfLastNonBlankLines(input.text, input.count); + if (input.mode === 'blocked') value = detectTerminalWaitBlockedReason(input.text); + if (input.mode === 'ready') value = isKnownReadyPromptPreview(input.text); + if (input.mode === 'producer-controls') { + const emulator = new HeadlessEmulator({ cols: 80, rows: 12, scrollback: 0 }); + try { + await emulator.write('\\r\\nordinary output\\r\\n'); + const raw = emulator.getVisibleLines(); + const visible = projectTerminalVisibleLines(emulator).lines; + const tail = projectTerminalTailLines(emulator, 12).lines; + const longLines = ['prefix', 'x'.repeat(299)]; + const preview = buildPreview(longLines, ''); + const waitText = buildTerminalWaitText(longLines, '', preview); + value = { + rawRowsStartBlank: raw[0] === '', + visibleRows: visible, + projectedTail: tail, + visibleClassification: detectTerminalWaitBlockedReason(visible.join('\\n')), + ordinaryTailClassification: detectTerminalWaitBlockedReason(buildTerminalWaitText(raw, '', '')), + clippedPreviewStartsNewline: preview.startsWith('\\n'), + retainedTailStartsNewline: waitText.startsWith('\\n'), + retainedTailClassification: detectTerminalWaitBlockedReason(waitText), + emptyTailFallbackStartsNewline: buildTerminalWaitText([], '', preview).startsWith('\\n') + }; + } finally { emulator.dispose(); } + } + process.stdout.write(JSON.stringify({ phase: 'returned', value }) + '\\n'); + } + main().catch(error => { process.stderr.write(String(error)); process.exitCode = 1; }); + ` + const cases = [ + { name: 'leading newline only', mode: 'window', text: '\n', count: 12, expected: 0 }, + { name: 'leading newline and text', mode: 'window', text: '\ntext', count: 12, expected: 0 }, + { name: 'blank screen classification', mode: 'blocked', text: '\n\n', expected: null }, + { + name: 'leading blank trust dialog', + mode: 'blocked', + text: '\nDo you trust this workspace directory?\n1. Yes\n2. No', + expected: 'agent-trust-workspace' + }, + { + name: 'leading blank ready header', + mode: 'ready', + text: '\nOpenAI Codex\nmodel: test\ndirectory: /workspace', + expected: true + }, + { + name: 'enough nonblank rows', + mode: 'window', + text: '\nfirst\nsecond', + count: 1, + expected: 7 + }, + { + name: 'production producer controls', + mode: 'producer-controls', + expected: { + rawRowsStartBlank: true, + visibleRows: ['ordinary output'], + projectedTail: ['ordinary output'], + visibleClassification: null, + ordinaryTailClassification: null, + clippedPreviewStartsNewline: true, + retainedTailStartsNewline: false, + retainedTailClassification: null, + emptyTailFallbackStartsNewline: true + } + } + ] + const results = {} + for (const [label, source] of Object.entries({ before: baseline, after: current })) { + const childPath = join(scratch, `${label}.cjs`) + await build({ + stdin: { contents: entry, resolveDir: root }, + outfile: childPath, + bundle: true, + platform: 'node', + format: 'cjs', + logLevel: 'silent', + plugins: [ + { + name: 'terminal-wait-baseline', + setup(builder) { + builder.onLoad({ filter: /terminal-wait-tail-window\.ts$/ }, (args) => + resolve(args.path) === absoluteSource ? { contents: source, loader: 'ts' } : null + ) + } + } + ] + }) + results[label] = [] + for (const input of cases) { + let childTerminated = false + const started = performance.now() + const result = await runProcess({ + program: process.execPath, + args: ['--max-old-space-size=128', childPath, JSON.stringify(input)], + env: { ...process.env, ORCA_BACKGROUND_LAUNCH: '1' }, + timeoutMs: 2_000, + maxOutputBytes: 8192, + onChildTerminated: () => { + childTerminated = true + } + }) + const output = result.stdout + .trim() + .split('\n') + .filter(Boolean) + .map((line) => JSON.parse(line)) + const expectedTimeout = label === 'before' && cases.indexOf(input) < 5 + const returned = output.find((event) => event.phase === 'returned') + if ( + !childTerminated || + result.timedOut !== expectedTimeout || + !output.some((event) => event.phase === 'entered') + ) { + throw new Error(`Unexpected ${label} result for ${input.name}: ${JSON.stringify(result)}`) + } + if ( + !expectedTimeout && + (result.code !== 0 || + !returned || + ('expected' in input && !isDeepStrictEqual(returned.value, input.expected))) + ) { + throw new Error( + `Unexpected ${label} output for ${input.name}: ${result.stdout} ${result.stderr}` + ) + } + results[label].push({ + name: input.name, + timedOut: result.timedOut, + childTerminated, + code: result.code, + signal: result.signal, + elapsedMs: Math.round(performance.now() - started), + ...(returned ? { value: returned.value } : {}) + }) + } + } + const tag = await runProcess({ + program: 'git', + args: ['show', `v1.4.198:${sourcePath}`], + cwd: root, + maxOutputBytes: 16_384 + }) + const provenance = await runProcess({ + program: 'git', + args: ['rev-parse', 'HEAD'], + cwd: root, + maxOutputBytes: 1024 + }) + process.stdout.write( + `${JSON.stringify( + { + node: process.version, + platform: process.platform, + architecture: process.arch, + revision: provenance.stdout.trim(), + source: sourcePath, + hashes: { + before: sha256(baseline), + after: sha256(current), + reportedVersion: tag.code === 0 ? sha256(tag.stdout) : null + }, + supportingSourceHashes, + reportedVersionSourceMatchesBaseline: tag.code === 0 && tag.stdout === baseline, + childTimeoutMs: 2000, + childHeapLimitMiB: 128, + scope: + 'Helper termination and producer controls; no incident attribution or retained-byte claim.', + results + }, + null, + 2 + )}\n` + ) +} finally { + if (runnerId) { + delete require.cache[runnerId] + } + await rm(scratch, { recursive: true, force: true }) +} diff --git a/docs/audits/terminal-wait-leading-blank/results.json b/docs/audits/terminal-wait-leading-blank/results.json new file mode 100644 index 00000000000..98776e8b5b8 --- /dev/null +++ b/docs/audits/terminal-wait-leading-blank/results.json @@ -0,0 +1,172 @@ +{ + "node": "v26.6.0", + "platform": "darwin", + "architecture": "arm64", + "revision": "9558152a04c499c666192dd08906be8ada09e1dc", + "source": "src/main/runtime/terminal-wait-tail-window.ts", + "hashes": { + "before": "e6bc10e924d45bf4bfbd6fabf4404f160107cf7235b11c4e140cd6c78cff9146", + "after": "8df53ea8f92461549d727f251dae7cb4415d4208228ec5f60df9dcf13b863b86", + "reportedVersion": "e6bc10e924d45bf4bfbd6fabf4404f160107cf7235b11c4e140cd6c78cff9146" + }, + "supportingSourceHashes": { + "src/main/runtime/terminal-wait-detection.ts": "226d09d2d8f7f1d692fb8ba1ed340818e6bd402b74d7bd98d86b1868a09503f0", + "src/main/runtime/orca-runtime-terminal-projection.ts": "70b815cf26864719b30b64845c0035093c16b9d85cb4bb8d25c4ae5fa63c3026", + "src/main/runtime/terminal-tail-read.ts": "3517bb22b9bf4bdf2f3acee8ceac9ca71221964cfa3f16be1f7b2ad22212b476", + "src/main/runtime/terminal-tail-state.ts": "a6f41a683d5f03023e4f6d20d653ebf069b036f82100c908a775a1890c49b2ef", + "src/main/runtime/terminal-wait-tail-state.ts": "61acc15fb8b9ce7faa0a2df80a7b6bae09dfed103c15b9c6c35a66d6ad585db1", + "src/main/daemon/headless-emulator.ts": "ace96102285c3967cd9544e7696fe73e1ac6df576b476939aa9819f5c298bab6", + "src/main/runtime/terminal-wait-tail-window.test.ts": "0faab1ca7b01e43ab555fd7fe2979975b818c22f25eca4f74dea3eb04f02c66b" + }, + "reportedVersionSourceMatchesBaseline": true, + "childTimeoutMs": 2000, + "childHeapLimitMiB": 128, + "scope": "Helper termination and producer controls; no incident attribution or retained-byte claim.", + "results": { + "before": [ + { + "name": "leading newline only", + "timedOut": true, + "childTerminated": true, + "code": null, + "signal": "SIGTERM", + "elapsedMs": 2004 + }, + { + "name": "leading newline and text", + "timedOut": true, + "childTerminated": true, + "code": null, + "signal": "SIGTERM", + "elapsedMs": 2002 + }, + { + "name": "blank screen classification", + "timedOut": true, + "childTerminated": true, + "code": null, + "signal": "SIGTERM", + "elapsedMs": 2002 + }, + { + "name": "leading blank trust dialog", + "timedOut": true, + "childTerminated": true, + "code": null, + "signal": "SIGTERM", + "elapsedMs": 2002 + }, + { + "name": "leading blank ready header", + "timedOut": true, + "childTerminated": true, + "code": null, + "signal": "SIGTERM", + "elapsedMs": 2002 + }, + { + "name": "enough nonblank rows", + "timedOut": false, + "childTerminated": true, + "code": 0, + "signal": null, + "elapsedMs": 32, + "value": 7 + }, + { + "name": "production producer controls", + "timedOut": false, + "childTerminated": true, + "code": 0, + "signal": null, + "elapsedMs": 36, + "value": { + "rawRowsStartBlank": true, + "visibleRows": ["ordinary output"], + "projectedTail": ["ordinary output"], + "visibleClassification": null, + "ordinaryTailClassification": null, + "clippedPreviewStartsNewline": true, + "retainedTailStartsNewline": false, + "retainedTailClassification": null, + "emptyTailFallbackStartsNewline": true + } + } + ], + "after": [ + { + "name": "leading newline only", + "timedOut": false, + "childTerminated": true, + "code": 0, + "signal": null, + "elapsedMs": 34, + "value": 0 + }, + { + "name": "leading newline and text", + "timedOut": false, + "childTerminated": true, + "code": 0, + "signal": null, + "elapsedMs": 33, + "value": 0 + }, + { + "name": "blank screen classification", + "timedOut": false, + "childTerminated": true, + "code": 0, + "signal": null, + "elapsedMs": 33, + "value": null + }, + { + "name": "leading blank trust dialog", + "timedOut": false, + "childTerminated": true, + "code": 0, + "signal": null, + "elapsedMs": 33, + "value": "agent-trust-workspace" + }, + { + "name": "leading blank ready header", + "timedOut": false, + "childTerminated": true, + "code": 0, + "signal": null, + "elapsedMs": 33, + "value": true + }, + { + "name": "enough nonblank rows", + "timedOut": false, + "childTerminated": true, + "code": 0, + "signal": null, + "elapsedMs": 32, + "value": 7 + }, + { + "name": "production producer controls", + "timedOut": false, + "childTerminated": true, + "code": 0, + "signal": null, + "elapsedMs": 37, + "value": { + "rawRowsStartBlank": true, + "visibleRows": ["ordinary output"], + "projectedTail": ["ordinary output"], + "visibleClassification": null, + "ordinaryTailClassification": null, + "clippedPreviewStartsNewline": true, + "retainedTailStartsNewline": false, + "retainedTailClassification": null, + "emptyTailFallbackStartsNewline": true + } + } + ] + } +} diff --git a/src/main/runtime/terminal-wait-tail-window.test.ts b/src/main/runtime/terminal-wait-tail-window.test.ts new file mode 100644 index 00000000000..22a71140ee8 --- /dev/null +++ b/src/main/runtime/terminal-wait-tail-window.test.ts @@ -0,0 +1,77 @@ +import { mkdtemp, rm } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { build } from 'esbuild' +import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import { runProcess } from '../../shared/child-process/run-process' +import { startOfLastNonBlankLines } from './terminal-wait-tail-window' + +let scratch = '' +let childPath = '' + +beforeAll(async () => { + scratch = await mkdtemp(join(tmpdir(), 'orca-tail-window-')) + childPath = join(scratch, 'leading-blank.cjs') + await build({ + stdin: { + contents: ` + import { startOfLastNonBlankLines } from './src/main/runtime/terminal-wait-tail-window'; + import { detectTerminalWaitBlockedReason, isKnownReadyPromptPreview } from './src/main/runtime/terminal-wait-detection'; + const tails = ['', '\\n', '\\n\\n', '\\ntext', '\\ntext\\n', '\\n \\t\\ntext\\n\\n']; + process.stdout.write(JSON.stringify({ + offsets: tails.map(value => startOfLastNonBlankLines(value, 12)), + blank: detectTerminalWaitBlockedReason('\\n\\n'), + ordinary: detectTerminalWaitBlockedReason('\\nordinary output'), + blocked: detectTerminalWaitBlockedReason('\\nDo you trust this workspace directory?\\n1. Yes\\n2. No'), + ready: isKnownReadyPromptPreview('\\nOpenAI Codex\\nmodel: test\\ndirectory: /workspace') + })); + `, + resolveDir: process.cwd() + }, + outfile: childPath, + bundle: true, + platform: 'node', + format: 'cjs', + logLevel: 'silent' + }) +}) + +afterAll(async () => { + if (scratch) { + await rm(scratch, { recursive: true, force: true }) + } +}) + +describe('terminal wait nonblank tail window', () => { + it('terminates on leading blank rows before classifying the remaining screen', async () => { + // Isolate the synchronous regression so its timeout cannot block the test worker. + const result = await runProcess({ + program: process.execPath, + args: ['--max-old-space-size=64', childPath], + env: { ...process.env, ORCA_BACKGROUND_LAUNCH: '1' }, + timeoutMs: 2_000, + maxOutputBytes: 4096 + }) + expect(result.timedOut).toBe(false) + expect(result.code, result.stderr).toBe(0) + expect(JSON.parse(result.stdout)).toEqual({ + offsets: [0, 0, 0, 0, 0, 0], + blank: null, + ordinary: null, + blocked: 'agent-trust-workspace', + ready: true + }) + }) + + it.each([ + { value: 'first\nsecond\nthird', count: 2, expected: 'second\nthird' }, + { value: 'first\n\n \t\nsecond\nthird\n', count: 2, expected: 'second\nthird\n' }, + { value: '\nfirst\nsecond', count: 1, expected: 'second' }, + { value: '\nfirst\nsecond', count: 2, expected: 'first\nsecond' }, + { value: 'first\nsecond', count: 3, expected: 'first\nsecond' }, + { value: 'first\nsecond\n\n', count: 1, expected: 'second\n\n' }, + { value: ' \t\r\nsecond', count: 2, expected: ' \t\r\nsecond' } + ])('selects the last $count nonblank rows of $value', ({ value, count, expected }) => { + expect(value.slice(startOfLastNonBlankLines(value, count))).toBe(expected) + }) +}) diff --git a/src/main/runtime/terminal-wait-tail-window.ts b/src/main/runtime/terminal-wait-tail-window.ts index 788000328f1..fec141f7cde 100644 --- a/src/main/runtime/terminal-wait-tail-window.ts +++ b/src/main/runtime/terminal-wait-tail-window.ts @@ -23,7 +23,7 @@ export function startOfLastLines(value: string, count: number): number { export function startOfLastNonBlankLines(value: string, count: number): number { let seen = 0 let lineEnd = value.length - for (;;) { + while (lineEnd > 0) { const lineStart = value.lastIndexOf('\n', lineEnd - 1) + 1 if (hasNonWhitespaceBetween(value, lineStart, lineEnd)) { seen += 1 @@ -36,6 +36,7 @@ export function startOfLastNonBlankLines(value: string, count: number): number { } lineEnd = lineStart - 1 } + return 0 } function hasNonWhitespaceBetween(value: string, start: number, end: number): boolean { From 5723c5baa9a4292d2b9ea86ea38ec6ffc91e38a1 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:31:34 -0700 Subject: [PATCH 070/168] fix(runtime): preserve exited PTY authority across queued graphs (#21011) * fix(pty): reconcile daemon exits after synthetic notifications * fix(runtime): preserve exited PTY authority across queued graphs * test(runtime): include shared socket fixture for graph reproduction * docs(memory): clarify graph reproduction dependency and source hashes --------- Co-authored-by: m4air --- .../queued-terminal-graph-exit/README.md | 58 + .../queued-terminal-graph-exit/fixture.ts | 231 +++ .../preserved-history-fixture.ts | 180 ++ .../queued-terminal-graph-exit/reproduce.mjs | 174 ++ .../queued-terminal-graph-exit/results.json | 1763 +++++++++++++++++ ...-runtime-mark-pty-liveness-unverifiable.ts | 4 + src/main/runtime/orca-runtime-on-pty-exit.ts | 10 +- .../runtime/orca-runtime-sync-window-graph.ts | 8 +- .../queued-terminal-graph-exit.test.ts | 251 +++ 9 files changed, 2670 insertions(+), 9 deletions(-) create mode 100644 docs/audits/queued-terminal-graph-exit/README.md create mode 100644 docs/audits/queued-terminal-graph-exit/fixture.ts create mode 100644 docs/audits/queued-terminal-graph-exit/preserved-history-fixture.ts create mode 100644 docs/audits/queued-terminal-graph-exit/reproduce.mjs create mode 100644 docs/audits/queued-terminal-graph-exit/results.json create mode 100644 src/main/runtime/queued-terminal-graph-exit.test.ts diff --git a/docs/audits/queued-terminal-graph-exit/README.md b/docs/audits/queued-terminal-graph-exit/README.md new file mode 100644 index 00000000000..ae5c6684b39 --- /dev/null +++ b/docs/audits/queued-terminal-graph-exit/README.md @@ -0,0 +1,58 @@ +# Queued renderer graph restores an exited pane owner + +This is a separate source-level explanation for part of [#19018](https://github.com/stablyai/orca/issues/19018). It reproduces an execution host certifying exit, followed by a queued renderer graph restoring that PTY's runtime `connected` flag and making the actual stable-pane resolver throw `terminal_pane_owner_conflict` against the successor's durable binding. + +## Run + +From the checkout, with installed dependencies: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node docs/audits/queued-terminal-graph-exit/reproduce.mjs /tmp/queued-terminal-graph-exit.json +``` + +The script uses the real renderer graph publisher, main `Store.persistPtyBinding`, runtime, daemon server, adapter, and local sockets. Only the subprocess and the IPC dispatch boundary are controlled. It creates temporary data/socket paths, runs hidden Node tests, and removes its scratch files. It does not launch an Electron window or install dependencies. The JSON records source hashes and excludes randomly allocated terminal handles/incarnations. + +## Ordering + +1. Publish the mounted predecessor's graph normally. +2. Capture its next unchanged publication at the IPC dispatch boundary. The real publisher sends the mounted leaf, `mobileSessionTabs: []`, and `unchangedMobileSessionWorktrees`. +3. While that publication is queued, spawn a successor and durably bind it to the same tab and leaf. +4. Deliver the predecessor's physical daemon EXIT. The runtime becomes disconnected with an `exited` verdict. +5. Deliver the already-captured graph, without reordering renderer publications. +6. Resolve the pane, query the owning daemon's fresh inventory, and publish once more. A second variant unmounts the renderer terminal before that inventory and remounts it afterward through the real registration/publisher API. + +The replacement-binding commit and the daemon EXIT can run while a renderer invocation is queued. The production spawn commit persists the binding before returning its reply (`ipc/pty/ipc/spawn-commit-persist.ts`); the graph publisher reads the mounted pane's transport independently. The fixture controls that ordering; it does not prove its frequency on the reporting machine. + +Retirement correctly refuses to delete the successor's durable binding. Before the fix, the retained surface coordinates then allow the old graph leaf to set the predecessor connected again. A healthy inventory contains only the successor, but the runtime sweep skips records that still have a graph leaf. The list's presentation can label that leaf disconnected while the underlying pane resolver still sees it connected. + +## Results + +| Variant | First queued graph restores predecessor | After unmount, inventory, and remount | Pane conflict | +| --------------- | --------------------------------------- | ------------------------------------- | ----------------- | +| Before | yes | yes | yes | +| Exit check only | no | yes | no at first check | +| Complete fix | no | no | no | + +All three variants also run an ordinary-exit control without a replacement; it remains retired throughout. The middle variant isolates why a weak inventory absence during a renderer mount gap must preserve an already-earned exit certificate. Without that mount gap, the retained disconnected leaf keeps the inventory sweep from forgetting the verdict. + +The fix reuses the existing liveness verdict registry to keep an exited graph leaf disconnected and nonwritable, and skips recreating its PTY/URL-watcher ownership. It preserves surface membership: a separate actual `stopExactTerminalsForWorktree({ keepHistory: true })` control passes a changed renderer-built mobile snapshot while physical exit has completed but the stop reply is pending. The history surface remains present before and after the renderer clears its PTY binding. All phases check this control; the fixed phase also checks that mobile projection never combines one PTY's ID with another PTY's handle. + +Fresh spawn/registration clears the prior verdict; an owning inventory can establish `live`. A physical exit still records its certificate if the bounded PTY archive was already pruned. Host-only tests cover same-ID replacements, stale predecessor EXIT, fresh renderer-only panes, physical negative exit codes, local unverified stops, SSH disconnects, and retained history. The portable proof runs 15 actual-runtime cases across its three source variants. + +## Limits and version evidence + +The relevant graph admission, unconditional graph-connected write, durable retirement refusal, and inventory leaf exception are present in the reported **v1.4.197**. That tag already passes `providerExitObserved` from local and daemon physical exit callbacks and computes `processDeathCertified`; this proof's natural-exit path does not depend on #21000's synthetic-notification correction. Executable before/after runs use the current checkout with narrowly asserted source transforms, not the complete historical binary. + +This proves stale runtime/pane ownership, not a measured native-process or heap leak. The graph alone does not recreate a headless terminal model. It does not establish that every missing diagnostics row denotes an exited process, nor explain all handle-count growth in the issue. + +The existing register retains at most 256 unowned verdicts; PTY/handle/leaf owners keep their verdicts until their own lifecycle ends. The disconnected PTY archive is capped at 128. After both a record and its bounded verdict are evicted, the graph has no remaining per-ID certificate; this change does not add permanent tombstones. Later loss-of-contact writes can still replace an exit verdict with `unverifiable`; a stale positive inventory can separately write a connected record. Those paths are not exercised or fixed by this local queued-publication proof. + +A separate audit found that an unreachable unrelated legacy daemon can make aggregate exact-stop verification fail despite absence on the target's own daemon. That is excluded from this fix and from the proof's healthy target-inventory assertion. + +## Recorded run provenance + +`results.json` records the graph fix before the separate provider-inventory lifecycle fence in #21014. Its source hashes identify that earlier run; they are not a claim that every later audit commit has the same bytes. The reproduction can be rerun against the combined worktree. + +## Pull request dependency + +The graph PR is stacked on #21000, reusing its daemon socket fixture and physical-exit delivery contract. The graph mechanism is separate; the stack makes the executable proof dependencies explicit. diff --git a/docs/audits/queued-terminal-graph-exit/fixture.ts b/docs/audits/queued-terminal-graph-exit/fixture.ts new file mode 100644 index 00000000000..e145040e98a --- /dev/null +++ b/docs/audits/queued-terminal-graph-exit/fixture.ts @@ -0,0 +1,231 @@ +import { vi } from 'vitest' +import assert from 'node:assert/strict' +import { mkdtempSync, rmSync } from 'node:fs' +import { join } from 'node:path' +import { tmpdir } from 'node:os' +import { startLateExitHarness } from '../../../src/main/ipc/pty/daemon-late-exit-test-fixture' +import { OrcaRuntimeService } from '../../../src/main/runtime/orca-runtime' +import { Store } from '../../../src/main/persistence/loading-store/store' +import { resolveStablePaneOwner } from '../../../src/main/ipc/pty/pane/stable-owner' +import { + registerRuntimeTerminalTab, + setRuntimeGraphStoreStateGetter, + setRuntimeGraphSyncEnabled +} from '../../../src/renderer/src/runtime/sync-runtime-graph' +import { graphState } from '../../../src/renderer/src/runtime/sync-runtime-graph/graph-state' +import { syncRuntimeGraph } from '../../../src/renderer/src/runtime/sync-runtime-graph/graph-publication' +import { makeState } from '../../../src/renderer/src/runtime/sync-runtime-graph-test-harness' +import { advertisedUrlWatcher } from '../../../src/main/ports/advertised-url-watcher' + +class QueuedGraphExitRuntime extends OrcaRuntimeService { + capture(id: string) { + const pty = this.ptysById.get(id) + return { + connected: pty?.connected, + exitCause: pty?.lastExitCause, + incarnationId: pty?.incarnationId, + liveness: this.getPtyLivenessVerdict(id), + model: this.headlessTerminals.has(id), + urlBound: advertisedUrlWatcher['ptyToWorktree'].has(id), + leaves: this.getLeavesForPty(id).map((leaf) => ({ + connected: leaf.connected, + writable: leaf.writable + })) + } + } + + mobile(worktreeId: string) { + return this.getMobileSessionTabsForWorktree(worktreeId).tabs.flatMap((tab) => + tab.type === 'terminal' + ? [ + { + ptyId: tab.ptyId, + handlePtyId: tab.terminal ? this.handles.get(tab.terminal)?.ptyId : null + } + ] + : [] + ) + } +} +export async function runQueuedGraphExitScenario( + replacement: boolean, + graphGapBeforeInventory = false +) { + const h = await startLateExitHarness() + const predecessor = h.subprocess + const dir = mkdtempSync(join(tmpdir(), 'orca-queued-owner-')) + const store = new Store({ dataFile: join(dir, 'orca-data.json') }) + const runtime = new QueuedGraphExitRuntime(store) + h.session.runtime = runtime + const WT = 'repo::/tmp/late-exit-audit' + const TAB = '00000000-0000-4000-8000-000000000001' + const LEAF = '00000000-0000-4000-8000-000000000002' + const successorId = `${WT}@@successor` + let unregister: (() => void) | undefined + try { + store.persistPtyBinding({ + worktreeId: WT, + tabId: TAB, + leafId: LEAF, + ptyId: h.id, + incarnationId: h.result.incarnationId + }) + runtime.registerPty(h.id, WT, null, { + tabId: TAB, + leafId: LEAF, + incarnationId: h.result.incarnationId + }) + runtime.setPtyController({ + write: () => true, + kill: () => true, + getForegroundProcess: async () => null, + listProcesses: (_connection, opts) => h.adapter.listProcesses(opts), + hasPty: (id) => h.adapter.hasPty(id) + }) + const state = makeState({ + tabsByWorktree: { + [WT]: [ + { + id: TAB, + worktreeId: WT, + title: 'Terminal', + ptyId: h.id, + customTitle: null, + color: null, + sortOrder: 0, + createdAt: 1 + } + ] + }, + terminalLayoutsByTabId: { + [TAB]: { + root: { type: 'leaf', leafId: LEAF }, + activeLeafId: LEAF, + expandedLeafId: null, + ptyIdsByLeafId: { [LEAF]: h.id } + } + } + }) + const manager = { + getPanes: () => [{ id: 1, leafId: LEAF }], + getActivePane: () => ({ id: 1, leafId: LEAF }), + getLeafId: () => LEAF, + getNumericIdForLeaf: () => 1 + } + setRuntimeGraphSyncEnabled(false) + setRuntimeGraphStoreStateGetter(() => state) + vi.stubGlobal('HTMLElement', class HTMLElement {}) + let deliver: (() => void) | undefined + let capture: unknown + let queue = false + vi.stubGlobal('window', { + api: { + runtime: { + syncWindowGraph: (graph: never) => { + if (!queue) { + return Promise.resolve(runtime.syncWindowGraph(1, graph)) + } + capture = structuredClone(graph) + return new Promise((resolve) => { + deliver = () => resolve(runtime.syncWindowGraph(1, graph)) + }) + } + } + } + }) + const mount = () => + registerRuntimeTerminalTab({ + tabId: TAB, + worktreeId: WT, + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the publisher reads only the four pane lookup methods supplied by this headless manager. + getManager: () => manager as never, + getContainer: () => null, + getPtyIdForPane: () => h.id, + getTabWideAgentHintLeafId: () => null + }) + unregister = mount() + const publish = (): Promise => { + graphState.syncEnabled = true + const pending = syncRuntimeGraph() + graphState.syncEnabled = false + return pending + } + await publish() + queue = true + const inFlight = publish() + assert(deliver, 'Publisher did not dispatch its graph') + if (replacement) { + const next = await h.adapter.spawn({ cols: 80, rows: 24, sessionId: successorId }) + assert( + store.persistPtyBinding({ + worktreeId: WT, + tabId: TAB, + leafId: LEAF, + ptyId: next.id, + incarnationId: next.incarnationId + }) + ) + runtime.registerPty(next.id, WT, null, { + tabId: TAB, + leafId: LEAF, + incarnationId: next.incarnationId + }) + } + predecessor._simulateExit(0) + await h.waitForExit() + const afterExit = runtime.capture(h.id) + assert.equal(afterExit.connected, false) + deliver() + await inFlight + const afterQueuedGraph = runtime.capture(h.id) + let resolution: unknown + try { + resolution = resolveStablePaneOwner(runtime, store, `${TAB}:${LEAF}`, WT, null) + } catch (e) { + resolution = e instanceof Error ? e.message : String(e) + } + queue = false + if (graphGapBeforeInventory) { + unregister() + unregister = undefined + await publish() + } + const list = await runtime.listTerminals() + const afterFreshList = runtime.capture(h.id) + if (graphGapBeforeInventory) { + unregister = mount() + } + await publish() + return { + scenario: replacement ? 'successor-binding-before-exit' : 'ordinary-exit', + graphGapBeforeInventory, + capture, + afterExit, + afterQueuedGraph, + afterFreshList, + afterRepeatedGraph: runtime.capture(h.id), + mobile: runtime.mobile(WT), + resolution, + inventory: await h.adapter.listProcesses(), + persistedPtyId: + store.getWorkspaceSession().terminalLayoutsByTabId[TAB]?.ptyIdsByLeafId?.[LEAF], + listed: list.terminals.map((t) => ({ + id: t.ptyId, + connected: t.connected, + tabId: t.tabId, + leafId: t.leafId + })) + } + } finally { + graphState.syncEnabled = false + unregister?.() + setRuntimeGraphSyncEnabled(false) + setRuntimeGraphStoreStateGetter(null) + vi.unstubAllGlobals() + runtime.onPtyExit(h.id, 0) + runtime.onPtyExit(successorId, 0) + await h.dispose() + store.flushOrThrow() + rmSync(dir, { recursive: true, force: true }) + } +} diff --git a/docs/audits/queued-terminal-graph-exit/preserved-history-fixture.ts b/docs/audits/queued-terminal-graph-exit/preserved-history-fixture.ts new file mode 100644 index 00000000000..fd904d79ca1 --- /dev/null +++ b/docs/audits/queued-terminal-graph-exit/preserved-history-fixture.ts @@ -0,0 +1,180 @@ +import { expect, vi } from 'vitest' +import { OrcaRuntimeService } from '../../../src/main/runtime/orca-runtime' +import { + buildMobileSessionTabSnapshots, + registerRuntimeTerminalTab, + setRuntimeGraphStoreStateGetter, + setRuntimeGraphSyncEnabled +} from '../../../src/renderer/src/runtime/sync-runtime-graph' +import { makeState } from '../../../src/renderer/src/runtime/sync-runtime-graph-test-harness' +import { graphState } from '../../../src/renderer/src/runtime/sync-runtime-graph/graph-state' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('background required') +} + +const TAB = '10000000-0000-4000-8000-000000000001' +const TEST_WORKTREE_PATH = '/tmp/worktree-a' +const TEST_WORKTREE_ID = `repo-1::${TEST_WORKTREE_PATH}` +const LEAF = '10000000-0000-4000-8000-000000000002' +const INC = '10000000-0000-4000-8000-000000000003' +const PTY = `${TEST_WORKTREE_ID}@@sleep-review` + +export async function runPreservedHistoryScenario() { + const runtime = new OrcaRuntimeService() + const worktree = { + id: TEST_WORKTREE_ID, + path: TEST_WORKTREE_PATH, + repoId: 'repo-1', + name: 'worktree-a', + branch: 'main', + isMain: false + } + vi.spyOn(runtime, 'resolveWorktreeSelector').mockResolvedValue(worktree) + vi.spyOn(runtime, 'getResolvedWorktreeMap').mockResolvedValue( + new Map([[TEST_WORKTREE_ID, worktree]]) + ) + let stopped = false + let finishStop!: () => void + const stopGate = new Promise((resolve) => { + finishStop = resolve + }) + const stop = vi.fn(async () => { + runtime.onPtyExit(PTY, 0, INC, { providerExitObserved: true }) + stopped = true + await stopGate + return true + }) + runtime.setPtyController({ + write: () => true, + kill: () => true, + stopAndWait: stop, + getForegroundProcess: async () => null, + hasPty: () => !stopped, + listProcesses: async () => + stopped ? [] : [{ id: PTY, cwd: TEST_WORKTREE_PATH, title: 'terminal', incarnationId: INC }] + }) + let currentPty: string | null = PTY + const state = makeState({ + tabsByWorktree: { + [TEST_WORKTREE_ID]: [ + { + id: TAB, + worktreeId: TEST_WORKTREE_ID, + title: 'Terminal', + ptyId: PTY, + customTitle: null, + color: null, + sortOrder: 0, + createdAt: 1 + } + ] + }, + terminalLayoutsByTabId: { + [TAB]: { + root: { type: 'leaf', leafId: LEAF }, + activeLeafId: LEAF, + expandedLeafId: null, + ptyIdsByLeafId: { [LEAF]: PTY } + } + } + }) + const manager = { + getPanes: () => [{ id: 1, leafId: LEAF }], + getActivePane: () => ({ id: 1, leafId: LEAF }), + getLeafId: () => LEAF, + getNumericIdForLeaf: () => 1 + } + setRuntimeGraphSyncEnabled(false) + setRuntimeGraphStoreStateGetter(() => state) + const unregister = registerRuntimeTerminalTab({ + tabId: TAB, + worktreeId: TEST_WORKTREE_ID, + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the snapshot builder reads only these four supplied pane lookup methods. + getManager: () => manager as never, + getContainer: () => null, + getPtyIdForPane: () => currentPty, + getTabWideAgentHintLeafId: () => null + }) + const publish = () => { + const snapshots = buildMobileSessionTabSnapshots(state) + runtime.syncWindowGraph(1, { + tabs: [ + { + tabId: TAB, + worktreeId: TEST_WORKTREE_ID, + title: 'Terminal', + activeLeafId: LEAF, + layout: null + } + ], + leaves: [ + { + tabId: TAB, + worktreeId: TEST_WORKTREE_ID, + leafId: LEAF, + paneRuntimeId: 1, + ptyId: currentPty + } + ], + mobileSessionTabs: snapshots + }) + return snapshots[0] + } + const capture = () => + runtime['mobileSessionTabsByWorktree'].get(TEST_WORKTREE_ID)?.tabs.map((tab) => ({ + type: tab.type, + id: tab.id, + ptyId: tab.type === 'terminal' ? tab.ptyId : null + })) + try { + runtime.registerPty(PTY, TEST_WORKTREE_ID, null, { + tabId: TAB, + leafId: LEAF, + incarnationId: INC + }) + publish() + const initial = capture() + expect(initial).toHaveLength(1) + const pending = runtime.stopExactTerminalsForWorktree(`id:${TEST_WORKTREE_ID}`, [PTY], { + keepHistory: true, + targetOnly: true + }) + await vi.waitFor(() => expect(stop).toHaveBeenCalledOnce()) + const afterExit = capture() + expect(afterExit).toHaveLength(1) + state.runtimePaneTitlesByTabId = { [TAB]: { 1: 'Sleeping terminal' } } + const incoming = publish() + const afterQueued = capture() + const queuedLeaf = runtime['leaves'].get(runtime['getLeafKey'](TAB, LEAF)) + const leafState = queuedLeaf + ? { connected: queuedLeaf.connected, writable: queuedLeaf.writable, ptyId: queuedLeaf.ptyId } + : null + const model = runtime['headlessTerminals'].has(PTY) + finishStop() + const result = await pending + currentPty = null + state.tabsByWorktree[TEST_WORKTREE_ID] = [ + { ...state.tabsByWorktree[TEST_WORKTREE_ID][0], ptyId: null } + ] + state.terminalLayoutsByTabId[TAB] = { ...state.terminalLayoutsByTabId[TAB], ptyIdsByLeafId: {} } + publish() + return { + initial, + afterExit, + incoming, + afterQueued, + leafState, + model, + afterBindingClear: capture(), + result + } + } finally { + finishStop() + graphState.syncEnabled = false + unregister() + runtime.onPtyExit(PTY, 0, INC) + setRuntimeGraphStoreStateGetter(null) + vi.restoreAllMocks() + } +} diff --git a/docs/audits/queued-terminal-graph-exit/reproduce.mjs b/docs/audits/queued-terminal-graph-exit/reproduce.mjs new file mode 100644 index 00000000000..71e3ffede5a --- /dev/null +++ b/docs/audits/queued-terminal-graph-exit/reproduce.mjs @@ -0,0 +1,174 @@ +import assert from 'node:assert/strict' +import { createHash } from 'node:crypto' +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import { startVitest } from 'vitest/node' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1.') +} +const root = fileURLToPath(new URL('../../../', import.meta.url)) +const paths = [ + 'src/main/runtime/orca-runtime-sync-window-graph.ts', + 'src/main/runtime/orca-runtime-mark-pty-liveness-unverifiable.ts', + 'src/main/runtime/orca-runtime-on-pty-exit.ts' +] +const sources = await Promise.all(paths.map((path) => readFile(join(root, path), 'utf8'))) +const gate = ` // Retained history stays addressable, but a renderer graph cannot revoke a host-certified exit. + const connected = ptyId !== null && this.getPtyLivenessVerdict(ptyId)?.status !== 'exited' +` +const preserve = ` // An inventory's weak absence cannot revoke an earlier host-certified exit. + if (tracked?.verdict.status === 'exited') { + return + } +` +const certificate = ` if (processDeathCertified) { + // The bounded verdict register also fences late graphs after the PTY record was pruned. + this.rememberPtyLivenessVerdict(ptyId, { status: 'exited' }) + } +` +for (const [index, text] of [gate, preserve, certificate].entries()) { + assert(sources[index].includes(text), 'Source changed: review the baseline transform.') +} +const baseline = [ + sources[0] + .replace(gate, '') + .replace( + " connected,\n writable: this.graphStatus === 'ready' && connected,", + " connected: ptyId !== null,\n writable: this.graphStatus === 'ready' && ptyId !== null," + ) + .replace(' if (leaf.ptyId && connected) {', ' if (leaf.ptyId) {'), + sources[1].replace(preserve, ''), + sources[2].replace(certificate, '').replace( + ' pty.lastExitCause = exitCause\n', + ` pty.lastExitCause = exitCause + if (exitCode >= 0 || options.hostExitConfirmed === true) { + this.rememberPtyLivenessVerdict(ptyId, { status: 'exited' }) + } +` + ) +] +const scratch = await mkdtemp(join(tmpdir(), 'orca-queued-graph-proof-')) +const phases = [] +try { + for (const phase of ['before', 'guard-only', 'after']) { + const outputPath = join(scratch, `${phase}.json`) + const testPath = join(scratch, `${phase}.test.ts`) + const configPath = join(scratch, `${phase}.config.mjs`) + await writeFile( + testPath, + ` +import { afterAll, it } from ${JSON.stringify(join(root, 'node_modules/vitest/dist/index.js'))} +import { writeFileSync } from 'node:fs' +import { runQueuedGraphExitScenario } from ${JSON.stringify(join(root, 'docs/audits/queued-terminal-graph-exit/fixture.ts'))} +import { runPreservedHistoryScenario } from ${JSON.stringify(join(root, 'docs/audits/queued-terminal-graph-exit/preserved-history-fixture.ts'))} +const rows = [] +let history +it("preserved history", async () => { history = await runPreservedHistoryScenario() }) +for (const successor of [false, true]) { + for (const graphGapBeforeInventory of [false, true]) { + it(String(successor) + String(graphGapBeforeInventory), async () => rows.push(await runQueuedGraphExitScenario(successor, graphGapBeforeInventory))) + } +} +afterAll(() => writeFileSync(${JSON.stringify(outputPath)}, JSON.stringify({ rows, history }))) +` + ) + const replacements = Object.fromEntries( + paths.map((path, index) => [ + `/${path}`, + phase === 'after' || (phase === 'guard-only' && index === 0) + ? sources[index] + : baseline[index] + ]) + ) + await writeFile( + configPath, + ` +import base from ${JSON.stringify(pathToFileURL(join(root, 'config/vitest.config.ts')).href)} +const replacements = ${JSON.stringify(replacements)} +export default { + ...base, + plugins: [{ name: 'graph-exit-baseline', enforce: 'pre', transform(code, id) { + for (const [path, replacement] of Object.entries(replacements)) { + if (id.replaceAll('\\\\', '/').endsWith(path)) return replacement + } + } }], + test: { ...base.test, include: [${JSON.stringify(testPath)}], maxWorkers: 1, fileParallelism: false } +} +` + ) + const runner = await startVitest('test', [], { + root, + config: configPath, + watch: false, + reporters: ['dot'] + }) + assert(runner, 'Vitest did not start') + await runner.close() + const { rows, history } = JSON.parse(await readFile(outputPath, 'utf8')) + assert.equal(history.afterQueued.length, 1) + assert.equal(history.afterBindingClear.length, 1) + assert.equal(history.model, false) + if (phase !== 'before') { + assert.equal(history.leafState.connected, false) + assert.equal(history.leafState.writable, false) + } + delete history.incoming.publicationEpoch + assert.equal(rows.length, 4) + for (const row of rows) { + const successor = row.scenario === 'successor-binding-before-exit' + assert.equal(row.afterExit.connected, false) + assert.equal(row.afterQueuedGraph.connected, successor && phase === 'before') + assert.equal( + row.afterRepeatedGraph.connected, + successor && (phase === 'before' || (phase === 'guard-only' && row.graphGapBeforeInventory)) + ) + assert.equal( + row.resolution === 'terminal_pane_owner_conflict', + successor && phase === 'before' + ) + assert.equal(row.inventory.length, successor ? 1 : 0) + assert.equal(row.afterRepeatedGraph.model, false) + if (phase === 'after') { + assert.equal(row.afterRepeatedGraph.urlBound, false) + assert(row.afterRepeatedGraph.leaves.every((leaf) => !leaf.connected && !leaf.writable)) + assert(row.mobile.every((tab) => !tab.handlePtyId || tab.ptyId === tab.handlePtyId)) + } + for (const state of [ + row.afterExit, + row.afterQueuedGraph, + row.afterFreshList, + row.afterRepeatedGraph + ]) { + delete state.incarnationId + } + delete row.capture.rendererGeneration + if (row.resolution && typeof row.resolution === 'object') { + row.resolution = { ptyId: row.resolution.ptyId } + } + row.inventory = row.inventory.map(({ id }) => ({ id })) + } + phases.push({ phase, rows, history }) + } + const output = `${JSON.stringify( + { + sources: Object.fromEntries( + paths.map((path, index) => [ + path, + createHash('sha256').update(sources[index]).digest('hex') + ]) + ), + phases + }, + null, + 2 + )}\n` + if (process.argv[2]) { + await writeFile(resolve(process.argv[2]), output) + } + process.stdout.write(output) +} finally { + await rm(scratch, { recursive: true, force: true }) +} diff --git a/docs/audits/queued-terminal-graph-exit/results.json b/docs/audits/queued-terminal-graph-exit/results.json new file mode 100644 index 00000000000..515d114eb1f --- /dev/null +++ b/docs/audits/queued-terminal-graph-exit/results.json @@ -0,0 +1,1763 @@ +{ + "sources": { + "src/main/runtime/orca-runtime-sync-window-graph.ts": "e9527f5964af3ed93ec4815096ebdcc181118f9edc76f34e1cc25d1fa67a5fb4", + "src/main/runtime/orca-runtime-mark-pty-liveness-unverifiable.ts": "ff5433a848e43f22560234c98e25122237f16bcda7668603dd2640eb86cec315", + "src/main/runtime/orca-runtime-on-pty-exit.ts": "def5d33f70a656b13c619db8b453ab2b72cade31c7078583c6f6f6ea8d3544e6" + }, + "phases": [ + { + "phase": "before", + "rows": [ + { + "scenario": "ordinary-exit", + "graphGapBeforeInventory": false, + "capture": { + "tabs": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "title": "Terminal", + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "layout": null + } + ], + "leaves": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "leafId": "00000000-0000-4000-8000-000000000002", + "paneRuntimeId": 1, + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "paneTitle": null, + "title": "Terminal" + } + ], + "mobileSessionTabs": [], + "unchangedMobileSessionWorktrees": ["repo::/tmp/late-exit-audit"] + }, + "afterExit": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterQueuedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterFreshList": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": null, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterRepeatedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": null, + "model": false, + "urlBound": false, + "leaves": [] + }, + "mobile": [], + "resolution": null, + "inventory": [], + "listed": [] + }, + { + "scenario": "ordinary-exit", + "graphGapBeforeInventory": true, + "capture": { + "tabs": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "title": "Terminal", + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "layout": null + } + ], + "leaves": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "leafId": "00000000-0000-4000-8000-000000000002", + "paneRuntimeId": 1, + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "paneTitle": null, + "title": "Terminal" + } + ], + "mobileSessionTabs": [ + { + "worktree": "repo::/tmp/late-exit-audit", + "publicationEpoch": "renderer:852007b5-0f95-44b0-9182-1dbd97a4ce2b", + "snapshotVersion": 4, + "activeGroupId": null, + "activeTabId": null, + "activeTabType": null, + "tabs": [ + { + "type": "terminal", + "id": "00000000-0000-4000-8000-000000000001::00000000-0000-4000-8000-000000000002", + "title": "Terminal", + "parentTabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002", + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "parentLayout": { + "root": { + "type": "leaf", + "leafId": "00000000-0000-4000-8000-000000000002" + }, + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "expandedLeafId": null, + "ptyIdsByLeafId": { + "00000000-0000-4000-8000-000000000002": "repo::/tmp/late-exit-audit@@terminal" + } + }, + "isActive": false + } + ] + } + ], + "unchangedMobileSessionWorktrees": [] + }, + "afterExit": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterQueuedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterFreshList": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": null, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterRepeatedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": null, + "model": false, + "urlBound": false, + "leaves": [] + }, + "mobile": [], + "resolution": null, + "inventory": [], + "listed": [] + }, + { + "scenario": "successor-binding-before-exit", + "graphGapBeforeInventory": false, + "capture": { + "tabs": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "title": "Terminal", + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "layout": null + } + ], + "leaves": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "leafId": "00000000-0000-4000-8000-000000000002", + "paneRuntimeId": 1, + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "paneTitle": null, + "title": "Terminal" + } + ], + "mobileSessionTabs": [ + { + "worktree": "repo::/tmp/late-exit-audit", + "publicationEpoch": "renderer:852007b5-0f95-44b0-9182-1dbd97a4ce2b", + "snapshotVersion": 4, + "activeGroupId": null, + "activeTabId": null, + "activeTabType": null, + "tabs": [ + { + "type": "terminal", + "id": "00000000-0000-4000-8000-000000000001::00000000-0000-4000-8000-000000000002", + "title": "Terminal", + "parentTabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002", + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "parentLayout": { + "root": { + "type": "leaf", + "leafId": "00000000-0000-4000-8000-000000000002" + }, + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "expandedLeafId": null, + "ptyIdsByLeafId": { + "00000000-0000-4000-8000-000000000002": "repo::/tmp/late-exit-audit@@terminal" + } + }, + "isActive": false + } + ] + } + ], + "unchangedMobileSessionWorktrees": [] + }, + "afterExit": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterQueuedGraph": { + "connected": true, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": true, + "leaves": [ + { + "connected": true, + "writable": true + } + ] + }, + "afterFreshList": { + "connected": true, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": true, + "leaves": [ + { + "connected": true, + "writable": true + } + ] + }, + "afterRepeatedGraph": { + "connected": true, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": true, + "leaves": [ + { + "connected": true, + "writable": true + } + ] + }, + "mobile": [ + { + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "handlePtyId": "repo::/tmp/late-exit-audit@@terminal" + } + ], + "resolution": "terminal_pane_owner_conflict", + "inventory": [ + { + "id": "repo::/tmp/late-exit-audit@@successor" + } + ], + "persistedPtyId": "repo::/tmp/late-exit-audit@@successor", + "listed": [ + { + "id": "repo::/tmp/late-exit-audit@@terminal", + "connected": false, + "tabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002" + }, + { + "id": "repo::/tmp/late-exit-audit@@successor", + "connected": true, + "tabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002" + } + ] + }, + { + "scenario": "successor-binding-before-exit", + "graphGapBeforeInventory": true, + "capture": { + "tabs": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "title": "Terminal", + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "layout": null + } + ], + "leaves": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "leafId": "00000000-0000-4000-8000-000000000002", + "paneRuntimeId": 1, + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "paneTitle": null, + "title": "Terminal" + } + ], + "mobileSessionTabs": [ + { + "worktree": "repo::/tmp/late-exit-audit", + "publicationEpoch": "renderer:852007b5-0f95-44b0-9182-1dbd97a4ce2b", + "snapshotVersion": 4, + "activeGroupId": null, + "activeTabId": null, + "activeTabType": null, + "tabs": [ + { + "type": "terminal", + "id": "00000000-0000-4000-8000-000000000001::00000000-0000-4000-8000-000000000002", + "title": "Terminal", + "parentTabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002", + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "parentLayout": { + "root": { + "type": "leaf", + "leafId": "00000000-0000-4000-8000-000000000002" + }, + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "expandedLeafId": null, + "ptyIdsByLeafId": { + "00000000-0000-4000-8000-000000000002": "repo::/tmp/late-exit-audit@@terminal" + } + }, + "isActive": false + } + ] + } + ], + "unchangedMobileSessionWorktrees": [] + }, + "afterExit": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterQueuedGraph": { + "connected": true, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": true, + "leaves": [ + { + "connected": true, + "writable": true + } + ] + }, + "afterFreshList": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": null, + "model": false, + "urlBound": true, + "leaves": [] + }, + "afterRepeatedGraph": { + "connected": true, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": null, + "model": false, + "urlBound": true, + "leaves": [ + { + "connected": true, + "writable": true + } + ] + }, + "mobile": [ + { + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "handlePtyId": "repo::/tmp/late-exit-audit@@terminal" + } + ], + "resolution": "terminal_pane_owner_conflict", + "inventory": [ + { + "id": "repo::/tmp/late-exit-audit@@successor" + } + ], + "persistedPtyId": "repo::/tmp/late-exit-audit@@successor", + "listed": [ + { + "id": "repo::/tmp/late-exit-audit@@successor", + "connected": true, + "tabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002" + } + ] + } + ], + "history": { + "initial": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review" + } + ], + "afterExit": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review" + } + ], + "incoming": { + "worktree": "repo-1::/tmp/worktree-a", + "snapshotVersion": 2, + "activeGroupId": null, + "activeTabId": null, + "activeTabType": null, + "tabs": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "title": "Sleeping terminal", + "parentTabId": "10000000-0000-4000-8000-000000000001", + "leafId": "10000000-0000-4000-8000-000000000002", + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review", + "parentLayout": { + "root": { + "type": "leaf", + "leafId": "10000000-0000-4000-8000-000000000002" + }, + "activeLeafId": "10000000-0000-4000-8000-000000000002", + "expandedLeafId": null, + "ptyIdsByLeafId": { + "10000000-0000-4000-8000-000000000002": "repo-1::/tmp/worktree-a@@sleep-review" + } + }, + "isActive": false + } + ] + }, + "afterQueued": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review" + } + ], + "leafState": { + "connected": true, + "writable": true, + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review" + }, + "model": false, + "afterBindingClear": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "ptyId": null + } + ], + "result": { + "stopped": 1, + "stoppedPtyIds": ["repo-1::/tmp/worktree-a@@sleep-review"], + "livePtyIds": ["repo-1::/tmp/worktree-a@@sleep-review"], + "postStopVerified": true + } + } + }, + { + "phase": "guard-only", + "rows": [ + { + "scenario": "ordinary-exit", + "graphGapBeforeInventory": false, + "capture": { + "tabs": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "title": "Terminal", + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "layout": null + } + ], + "leaves": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "leafId": "00000000-0000-4000-8000-000000000002", + "paneRuntimeId": 1, + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "paneTitle": null, + "title": "Terminal" + } + ], + "mobileSessionTabs": [], + "unchangedMobileSessionWorktrees": ["repo::/tmp/late-exit-audit"] + }, + "afterExit": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterQueuedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterFreshList": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": null, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterRepeatedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": null, + "model": false, + "urlBound": false, + "leaves": [] + }, + "mobile": [], + "resolution": null, + "inventory": [], + "listed": [] + }, + { + "scenario": "ordinary-exit", + "graphGapBeforeInventory": true, + "capture": { + "tabs": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "title": "Terminal", + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "layout": null + } + ], + "leaves": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "leafId": "00000000-0000-4000-8000-000000000002", + "paneRuntimeId": 1, + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "paneTitle": null, + "title": "Terminal" + } + ], + "mobileSessionTabs": [ + { + "worktree": "repo::/tmp/late-exit-audit", + "publicationEpoch": "renderer:3f6ce3d1-c184-4637-a5c1-14b30cd0b1b4", + "snapshotVersion": 4, + "activeGroupId": null, + "activeTabId": null, + "activeTabType": null, + "tabs": [ + { + "type": "terminal", + "id": "00000000-0000-4000-8000-000000000001::00000000-0000-4000-8000-000000000002", + "title": "Terminal", + "parentTabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002", + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "parentLayout": { + "root": { + "type": "leaf", + "leafId": "00000000-0000-4000-8000-000000000002" + }, + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "expandedLeafId": null, + "ptyIdsByLeafId": { + "00000000-0000-4000-8000-000000000002": "repo::/tmp/late-exit-audit@@terminal" + } + }, + "isActive": false + } + ] + } + ], + "unchangedMobileSessionWorktrees": [] + }, + "afterExit": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterQueuedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterFreshList": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": null, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterRepeatedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": null, + "model": false, + "urlBound": false, + "leaves": [] + }, + "mobile": [], + "resolution": null, + "inventory": [], + "listed": [] + }, + { + "scenario": "successor-binding-before-exit", + "graphGapBeforeInventory": false, + "capture": { + "tabs": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "title": "Terminal", + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "layout": null + } + ], + "leaves": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "leafId": "00000000-0000-4000-8000-000000000002", + "paneRuntimeId": 1, + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "paneTitle": null, + "title": "Terminal" + } + ], + "mobileSessionTabs": [ + { + "worktree": "repo::/tmp/late-exit-audit", + "publicationEpoch": "renderer:3f6ce3d1-c184-4637-a5c1-14b30cd0b1b4", + "snapshotVersion": 4, + "activeGroupId": null, + "activeTabId": null, + "activeTabType": null, + "tabs": [ + { + "type": "terminal", + "id": "00000000-0000-4000-8000-000000000001::00000000-0000-4000-8000-000000000002", + "title": "Terminal", + "parentTabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002", + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "parentLayout": { + "root": { + "type": "leaf", + "leafId": "00000000-0000-4000-8000-000000000002" + }, + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "expandedLeafId": null, + "ptyIdsByLeafId": { + "00000000-0000-4000-8000-000000000002": "repo::/tmp/late-exit-audit@@terminal" + } + }, + "isActive": false + } + ] + } + ], + "unchangedMobileSessionWorktrees": [] + }, + "afterExit": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterQueuedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterFreshList": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterRepeatedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "mobile": [ + { + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "handlePtyId": null + } + ], + "resolution": { + "ptyId": "repo::/tmp/late-exit-audit@@successor" + }, + "inventory": [ + { + "id": "repo::/tmp/late-exit-audit@@successor" + } + ], + "persistedPtyId": "repo::/tmp/late-exit-audit@@successor", + "listed": [ + { + "id": "repo::/tmp/late-exit-audit@@terminal", + "connected": false, + "tabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002" + }, + { + "id": "repo::/tmp/late-exit-audit@@successor", + "connected": true, + "tabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002" + } + ] + }, + { + "scenario": "successor-binding-before-exit", + "graphGapBeforeInventory": true, + "capture": { + "tabs": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "title": "Terminal", + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "layout": null + } + ], + "leaves": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "leafId": "00000000-0000-4000-8000-000000000002", + "paneRuntimeId": 1, + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "paneTitle": null, + "title": "Terminal" + } + ], + "mobileSessionTabs": [ + { + "worktree": "repo::/tmp/late-exit-audit", + "publicationEpoch": "renderer:3f6ce3d1-c184-4637-a5c1-14b30cd0b1b4", + "snapshotVersion": 4, + "activeGroupId": null, + "activeTabId": null, + "activeTabType": null, + "tabs": [ + { + "type": "terminal", + "id": "00000000-0000-4000-8000-000000000001::00000000-0000-4000-8000-000000000002", + "title": "Terminal", + "parentTabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002", + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "parentLayout": { + "root": { + "type": "leaf", + "leafId": "00000000-0000-4000-8000-000000000002" + }, + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "expandedLeafId": null, + "ptyIdsByLeafId": { + "00000000-0000-4000-8000-000000000002": "repo::/tmp/late-exit-audit@@terminal" + } + }, + "isActive": false + } + ] + } + ], + "unchangedMobileSessionWorktrees": [] + }, + "afterExit": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterQueuedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterFreshList": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": null, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterRepeatedGraph": { + "connected": true, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": null, + "model": false, + "urlBound": true, + "leaves": [ + { + "connected": true, + "writable": true + } + ] + }, + "mobile": [ + { + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "handlePtyId": "repo::/tmp/late-exit-audit@@terminal" + } + ], + "resolution": { + "ptyId": "repo::/tmp/late-exit-audit@@successor" + }, + "inventory": [ + { + "id": "repo::/tmp/late-exit-audit@@successor" + } + ], + "persistedPtyId": "repo::/tmp/late-exit-audit@@successor", + "listed": [ + { + "id": "repo::/tmp/late-exit-audit@@successor", + "connected": true, + "tabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002" + } + ] + } + ], + "history": { + "initial": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review" + } + ], + "afterExit": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review" + } + ], + "incoming": { + "worktree": "repo-1::/tmp/worktree-a", + "snapshotVersion": 2, + "activeGroupId": null, + "activeTabId": null, + "activeTabType": null, + "tabs": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "title": "Sleeping terminal", + "parentTabId": "10000000-0000-4000-8000-000000000001", + "leafId": "10000000-0000-4000-8000-000000000002", + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review", + "parentLayout": { + "root": { + "type": "leaf", + "leafId": "10000000-0000-4000-8000-000000000002" + }, + "activeLeafId": "10000000-0000-4000-8000-000000000002", + "expandedLeafId": null, + "ptyIdsByLeafId": { + "10000000-0000-4000-8000-000000000002": "repo-1::/tmp/worktree-a@@sleep-review" + } + }, + "isActive": false + } + ] + }, + "afterQueued": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review" + } + ], + "leafState": { + "connected": false, + "writable": false, + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review" + }, + "model": false, + "afterBindingClear": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "ptyId": null + } + ], + "result": { + "stopped": 1, + "stoppedPtyIds": ["repo-1::/tmp/worktree-a@@sleep-review"], + "livePtyIds": ["repo-1::/tmp/worktree-a@@sleep-review"], + "postStopVerified": true + } + } + }, + { + "phase": "after", + "rows": [ + { + "scenario": "ordinary-exit", + "graphGapBeforeInventory": false, + "capture": { + "tabs": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "title": "Terminal", + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "layout": null + } + ], + "leaves": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "leafId": "00000000-0000-4000-8000-000000000002", + "paneRuntimeId": 1, + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "paneTitle": null, + "title": "Terminal" + } + ], + "mobileSessionTabs": [], + "unchangedMobileSessionWorktrees": ["repo::/tmp/late-exit-audit"] + }, + "afterExit": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterQueuedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterFreshList": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterRepeatedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [] + }, + "mobile": [], + "resolution": null, + "inventory": [], + "listed": [] + }, + { + "scenario": "ordinary-exit", + "graphGapBeforeInventory": true, + "capture": { + "tabs": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "title": "Terminal", + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "layout": null + } + ], + "leaves": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "leafId": "00000000-0000-4000-8000-000000000002", + "paneRuntimeId": 1, + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "paneTitle": null, + "title": "Terminal" + } + ], + "mobileSessionTabs": [ + { + "worktree": "repo::/tmp/late-exit-audit", + "publicationEpoch": "renderer:34668074-f3d3-4e0f-bbd5-e31900482d81", + "snapshotVersion": 4, + "activeGroupId": null, + "activeTabId": null, + "activeTabType": null, + "tabs": [ + { + "type": "terminal", + "id": "00000000-0000-4000-8000-000000000001::00000000-0000-4000-8000-000000000002", + "title": "Terminal", + "parentTabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002", + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "parentLayout": { + "root": { + "type": "leaf", + "leafId": "00000000-0000-4000-8000-000000000002" + }, + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "expandedLeafId": null, + "ptyIdsByLeafId": { + "00000000-0000-4000-8000-000000000002": "repo::/tmp/late-exit-audit@@terminal" + } + }, + "isActive": false + } + ] + } + ], + "unchangedMobileSessionWorktrees": [] + }, + "afterExit": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterQueuedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterFreshList": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterRepeatedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [] + }, + "mobile": [], + "resolution": null, + "inventory": [], + "listed": [] + }, + { + "scenario": "successor-binding-before-exit", + "graphGapBeforeInventory": false, + "capture": { + "tabs": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "title": "Terminal", + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "layout": null + } + ], + "leaves": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "leafId": "00000000-0000-4000-8000-000000000002", + "paneRuntimeId": 1, + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "paneTitle": null, + "title": "Terminal" + } + ], + "mobileSessionTabs": [ + { + "worktree": "repo::/tmp/late-exit-audit", + "publicationEpoch": "renderer:34668074-f3d3-4e0f-bbd5-e31900482d81", + "snapshotVersion": 4, + "activeGroupId": null, + "activeTabId": null, + "activeTabType": null, + "tabs": [ + { + "type": "terminal", + "id": "00000000-0000-4000-8000-000000000001::00000000-0000-4000-8000-000000000002", + "title": "Terminal", + "parentTabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002", + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "parentLayout": { + "root": { + "type": "leaf", + "leafId": "00000000-0000-4000-8000-000000000002" + }, + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "expandedLeafId": null, + "ptyIdsByLeafId": { + "00000000-0000-4000-8000-000000000002": "repo::/tmp/late-exit-audit@@terminal" + } + }, + "isActive": false + } + ] + } + ], + "unchangedMobileSessionWorktrees": [] + }, + "afterExit": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterQueuedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterFreshList": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterRepeatedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "mobile": [ + { + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "handlePtyId": null + } + ], + "resolution": { + "ptyId": "repo::/tmp/late-exit-audit@@successor" + }, + "inventory": [ + { + "id": "repo::/tmp/late-exit-audit@@successor" + } + ], + "persistedPtyId": "repo::/tmp/late-exit-audit@@successor", + "listed": [ + { + "id": "repo::/tmp/late-exit-audit@@terminal", + "connected": false, + "tabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002" + }, + { + "id": "repo::/tmp/late-exit-audit@@successor", + "connected": true, + "tabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002" + } + ] + }, + { + "scenario": "successor-binding-before-exit", + "graphGapBeforeInventory": true, + "capture": { + "tabs": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "title": "Terminal", + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "layout": null + } + ], + "leaves": [ + { + "tabId": "00000000-0000-4000-8000-000000000001", + "worktreeId": "repo::/tmp/late-exit-audit", + "leafId": "00000000-0000-4000-8000-000000000002", + "paneRuntimeId": 1, + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "paneTitle": null, + "title": "Terminal" + } + ], + "mobileSessionTabs": [ + { + "worktree": "repo::/tmp/late-exit-audit", + "publicationEpoch": "renderer:34668074-f3d3-4e0f-bbd5-e31900482d81", + "snapshotVersion": 4, + "activeGroupId": null, + "activeTabId": null, + "activeTabType": null, + "tabs": [ + { + "type": "terminal", + "id": "00000000-0000-4000-8000-000000000001::00000000-0000-4000-8000-000000000002", + "title": "Terminal", + "parentTabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002", + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "parentLayout": { + "root": { + "type": "leaf", + "leafId": "00000000-0000-4000-8000-000000000002" + }, + "activeLeafId": "00000000-0000-4000-8000-000000000002", + "expandedLeafId": null, + "ptyIdsByLeafId": { + "00000000-0000-4000-8000-000000000002": "repo::/tmp/late-exit-audit@@terminal" + } + }, + "isActive": false + } + ] + } + ], + "unchangedMobileSessionWorktrees": [] + }, + "afterExit": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterQueuedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "afterFreshList": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [] + }, + "afterRepeatedGraph": { + "connected": false, + "exitCause": { + "kind": "exited", + "exitCode": 0 + }, + "liveness": { + "status": "exited" + }, + "model": false, + "urlBound": false, + "leaves": [ + { + "connected": false, + "writable": false + } + ] + }, + "mobile": [ + { + "ptyId": "repo::/tmp/late-exit-audit@@terminal", + "handlePtyId": null + } + ], + "resolution": { + "ptyId": "repo::/tmp/late-exit-audit@@successor" + }, + "inventory": [ + { + "id": "repo::/tmp/late-exit-audit@@successor" + } + ], + "persistedPtyId": "repo::/tmp/late-exit-audit@@successor", + "listed": [ + { + "id": "repo::/tmp/late-exit-audit@@successor", + "connected": true, + "tabId": "00000000-0000-4000-8000-000000000001", + "leafId": "00000000-0000-4000-8000-000000000002" + } + ] + } + ], + "history": { + "initial": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review" + } + ], + "afterExit": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review" + } + ], + "incoming": { + "worktree": "repo-1::/tmp/worktree-a", + "snapshotVersion": 2, + "activeGroupId": null, + "activeTabId": null, + "activeTabType": null, + "tabs": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "title": "Sleeping terminal", + "parentTabId": "10000000-0000-4000-8000-000000000001", + "leafId": "10000000-0000-4000-8000-000000000002", + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review", + "parentLayout": { + "root": { + "type": "leaf", + "leafId": "10000000-0000-4000-8000-000000000002" + }, + "activeLeafId": "10000000-0000-4000-8000-000000000002", + "expandedLeafId": null, + "ptyIdsByLeafId": { + "10000000-0000-4000-8000-000000000002": "repo-1::/tmp/worktree-a@@sleep-review" + } + }, + "isActive": false + } + ] + }, + "afterQueued": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review" + } + ], + "leafState": { + "connected": false, + "writable": false, + "ptyId": "repo-1::/tmp/worktree-a@@sleep-review" + }, + "model": false, + "afterBindingClear": [ + { + "type": "terminal", + "id": "10000000-0000-4000-8000-000000000001::10000000-0000-4000-8000-000000000002", + "ptyId": null + } + ], + "result": { + "stopped": 1, + "stoppedPtyIds": ["repo-1::/tmp/worktree-a@@sleep-review"], + "livePtyIds": ["repo-1::/tmp/worktree-a@@sleep-review"], + "postStopVerified": true + } + } + } + ] +} diff --git a/src/main/runtime/orca-runtime-mark-pty-liveness-unverifiable.ts b/src/main/runtime/orca-runtime-mark-pty-liveness-unverifiable.ts index e86e10e41c3..c4e4324e47d 100644 --- a/src/main/runtime/orca-runtime-mark-pty-liveness-unverifiable.ts +++ b/src/main/runtime/orca-runtime-mark-pty-liveness-unverifiable.ts @@ -137,6 +137,10 @@ export class OrcaRuntimeWithMarkPtyLivenessUnverifiable extends OrcaRuntimeWithO protected forgetPtyLivenessVerdict(ptyId: string, observedNoLaterThan?: number): void { const tracked = this.ptyLivenessVerdictByPtyId.get(ptyId) + // An inventory's weak absence cannot revoke an earlier host-certified exit. + if (tracked?.verdict.status === 'exited') { + return + } if (observedNoLaterThan !== undefined && tracked && tracked.observedAt > observedNoLaterThan) { return } diff --git a/src/main/runtime/orca-runtime-on-pty-exit.ts b/src/main/runtime/orca-runtime-on-pty-exit.ts index 6ddf877f87c..33e1dfc5078 100644 --- a/src/main/runtime/orca-runtime-on-pty-exit.ts +++ b/src/main/runtime/orca-runtime-on-pty-exit.ts @@ -198,6 +198,10 @@ export class OrcaRuntimeWithOnPtyExit extends OrcaRuntimeWithOnClientDisconnecte this.terminalDrivers.clear(ptyId) this.remoteDesktopFloor.clearPty(ptyId) this.disposeHeadlessTerminal(ptyId) + if (processDeathCertified) { + // The bounded verdict register also fences late graphs after the PTY record was pruned. + this.rememberPtyLivenessVerdict(ptyId, { status: 'exited' }) + } if (pty) { pty.connected = false pty.runtimeSessionOwned = false @@ -205,12 +209,6 @@ export class OrcaRuntimeWithOnPtyExit extends OrcaRuntimeWithOnClientDisconnecte pty.disconnectedAt = Date.now() pty.lastExitCode = exitCode pty.lastExitCause = exitCause - if (exitCode >= 0 || options.hostExitConfirmed === true) { - // Record the certificate rather than merely dropping the doubt: a reader that has to - // authorize a respawn cannot distinguish "the host reported this process gone" from "this - // runtime has never asked" if both are absence. - this.rememberPtyLivenessVerdict(ptyId, { status: 'exited' }) - } // Why: the exited process's live frames say nothing about a replacement. // A same-id respawn makes the leaf writable again before any new title, // so leaving this true would let push delivery type into the new process diff --git a/src/main/runtime/orca-runtime-sync-window-graph.ts b/src/main/runtime/orca-runtime-sync-window-graph.ts index e820d15130b..c7f261f88be 100644 --- a/src/main/runtime/orca-runtime-sync-window-graph.ts +++ b/src/main/runtime/orca-runtime-sync-window-graph.ts @@ -107,14 +107,16 @@ export class OrcaRuntimeWithSyncWindowGraph extends OrcaRuntimeWithAttachWindow ? existing.ptyGeneration + 1 : (existing?.ptyGeneration ?? 0) const existingPty = ptyId ? this.ptysById.get(ptyId) : undefined + // Retained history stays addressable, but a renderer graph cannot revoke a host-certified exit. + const connected = ptyId !== null && this.getPtyLivenessVerdict(ptyId)?.status !== 'exited' const tailSource = existing?.ptyId === ptyId ? existing : existingPty nextLeaves.set(leafKey, { ...leaf, ptyId, ptyGeneration, - connected: ptyId !== null, - writable: this.graphStatus === 'ready' && ptyId !== null, + connected, + writable: this.graphStatus === 'ready' && connected, lastOutputAt: tailSource?.lastOutputAt ?? null, lastExitCode: tailSource?.lastExitCode ?? null, lastExitCause: tailSource?.lastExitCause ?? null, @@ -138,7 +140,7 @@ export class OrcaRuntimeWithSyncWindowGraph extends OrcaRuntimeWithAttachWindow : graphSyncedAt }) - if (leaf.ptyId) { + if (leaf.ptyId && connected) { this.recordPtyWorktree(leaf.ptyId, leaf.worktreeId, { connected: true, lastOutputAt: existing?.ptyId === leaf.ptyId ? existing.lastOutputAt : null, diff --git a/src/main/runtime/queued-terminal-graph-exit.test.ts b/src/main/runtime/queued-terminal-graph-exit.test.ts new file mode 100644 index 00000000000..a8a7be5d92e --- /dev/null +++ b/src/main/runtime/queued-terminal-graph-exit.test.ts @@ -0,0 +1,251 @@ +import { describe, expect, it, vi } from 'vitest' +import { OrcaRuntimeService } from './orca-runtime' + +const WORKTREE = 'repo::/tmp/graph-exit' +const TAB = '10000000-0000-4000-8000-000000000001' +const LEAF = '10000000-0000-4000-8000-000000000002' +const PTY = `${WORKTREE}@@terminal` +const FIRST = '10000000-0000-4000-8000-000000000003' +const NEXT = '10000000-0000-4000-8000-000000000004' + +class ExitAuthorityRuntime extends OrcaRuntimeService { + override resolveWorktreeSelector(selector: string) { + return super.resolveWorktreeSelector(selector) + } + + override getResolvedWorktreeMap() { + return super.getResolvedWorktreeMap() + } + + capture(id = PTY) { + const pty = this.ptysById.get(id) + return { connected: pty?.connected, incarnationId: pty?.incarnationId } + } + + get verdictCount(): number { + return this.ptyLivenessVerdictByPtyId.size + } + + dropRecord(id = PTY): void { + this.dropDisconnectedPtyRecord(id) + } + + history() { + return { + surfaces: this.mobileSessionTabsByWorktree.get(WORKTREE)?.tabs.length, + leaves: this.getLeavesForPty(PTY).map((leaf) => ({ + connected: leaf.connected, + writable: leaf.writable + })), + model: this.headlessTerminals.has(PTY) + } + } +} + +function graph( + runtime: OrcaRuntimeService, + ptyId: string | null = PTY, + snapshotVersion?: number +): void { + runtime.syncWindowGraph(1, { + tabs: [ + { tabId: TAB, worktreeId: WORKTREE, title: 'terminal', activeLeafId: LEAF, layout: null } + ], + leaves: [{ tabId: TAB, worktreeId: WORKTREE, leafId: LEAF, paneRuntimeId: 1, ptyId }], + ...(snapshotVersion === undefined + ? {} + : { + mobileSessionTabs: [ + { + worktree: WORKTREE, + publicationEpoch: 'renderer:retained-history', + snapshotVersion, + activeGroupId: null, + activeTabId: `${TAB}::${LEAF}`, + activeTabType: 'terminal' as const, + tabs: [ + { + type: 'terminal' as const, + id: `${TAB}::${LEAF}`, + parentTabId: TAB, + leafId: LEAF, + ...(ptyId ? { ptyId } : {}), + title: 'Terminal', + isActive: true + } + ] + } + ] + }) + }) +} + +function register(runtime: OrcaRuntimeService, incarnationId = FIRST): void { + runtime.registerPty(PTY, WORKTREE, null, { tabId: TAB, leafId: LEAF, incarnationId }) +} + +describe('host exit authority over queued renderer graphs', () => { + it('keeps exact-stop history addressable through a changed snapshot before binding clears', async () => { + const runtime = new ExitAuthorityRuntime() + const git = { + path: '/tmp/graph-exit', + head: 'abc', + branch: 'main', + isBare: false, + isMainWorktree: false + } + const worktree = { + ...git, + git, + id: WORKTREE, + repoId: 'repo', + displayName: 'graph-exit', + comment: '', + linkedIssue: null, + linkedPR: null, + linkedLinearIssue: null, + isArchived: false, + isUnread: false, + isPinned: false, + sortOrder: 0, + lastActivityAt: 0, + parentWorktreeId: null, + childWorktreeIds: [], + lineage: null + } + const resolve = vi.spyOn(runtime, 'resolveWorktreeSelector').mockResolvedValue(worktree) + const map = vi + .spyOn(runtime, 'getResolvedWorktreeMap') + .mockResolvedValue(new Map([[WORKTREE, worktree]])) + let stopped = false + let finishStop!: () => void + const gate = new Promise((done) => { + finishStop = done + }) + const stop = vi.fn(async () => { + runtime.onPtyExit(PTY, 0, FIRST, { providerExitObserved: true }) + stopped = true + await gate + return true + }) + runtime.setPtyController({ + write: () => true, + kill: () => true, + stopAndWait: stop, + getForegroundProcess: async () => null, + hasPty: () => !stopped, + listProcesses: async () => + stopped ? [] : [{ id: PTY, incarnationId: FIRST, cwd: worktree.path, title: 'terminal' }] + }) + let pending: Promise | undefined + try { + register(runtime) + graph(runtime, PTY, 1) + pending = runtime.stopExactTerminalsForWorktree(`id:${WORKTREE}`, [PTY], { + keepHistory: true, + targetOnly: true + }) + await vi.waitFor(() => expect(stop).toHaveBeenCalledOnce()) + graph(runtime, PTY, 2) + expect(runtime.history()).toEqual({ + surfaces: 1, + leaves: [{ connected: false, writable: false }], + model: false + }) + finishStop() + await expect(pending).resolves.toMatchObject({ postStopVerified: true }) + graph(runtime, null, 3) + expect(runtime.history().surfaces).toBe(1) + } finally { + finishStop() + await pending + resolve.mockRestore() + map.mockRestore() + runtime.onPtyExit(PTY, 0, FIRST) + } + }) + + it.each([0, -1])('retains a physical exit certificate after record pruning, code=%s', (code) => { + const runtime = new ExitAuthorityRuntime() + register(runtime) + runtime.dropRecord() + runtime.onPtyExit(PTY, code, FIRST, { providerExitObserved: true }) + graph(runtime) + expect(runtime.capture().connected).toBeUndefined() + expect(runtime.getPtyLivenessVerdict(PTY)).toEqual({ status: 'exited' }) + }) + + it('admits a new renderer pane without inventing a host verdict', () => { + const runtime = new ExitAuthorityRuntime() + graph(runtime) + expect(runtime.capture().connected).toBe(true) + expect(runtime.getPtyLivenessVerdict(PTY)).toBeNull() + }) + + it('admits a registered successor and ignores the predecessor exit', () => { + const runtime = new ExitAuthorityRuntime() + register(runtime) + runtime.onPtyExit(PTY, 0, FIRST) + register(runtime, NEXT) + runtime.onPtyExit(PTY, 0, FIRST) + graph(runtime) + expect(runtime.capture()).toEqual({ connected: true, incarnationId: NEXT }) + expect(runtime.getPtyLivenessVerdict(PTY)).toBeNull() + }) + + it('admits a same-ID spawn before its registration commits', () => { + const runtime = new ExitAuthorityRuntime() + register(runtime) + runtime.onPtyExit(PTY, 0, FIRST) + runtime.onPtySpawned(PTY, NEXT) + graph(runtime) + expect(runtime.capture()).toEqual({ connected: true, incarnationId: NEXT }) + expect(runtime.getPtyLivenessVerdict(PTY)).toBeNull() + }) + + it('allows owning inventory to prove the same ID live again', async () => { + const runtime = new ExitAuthorityRuntime() + register(runtime) + runtime.onPtyExit(PTY, 0, FIRST) + runtime.setPtyController({ + write: () => true, + kill: () => true, + getForegroundProcess: async () => null, + listProcesses: async () => [{ id: PTY, incarnationId: NEXT, cwd: '', title: 'terminal' }] + }) + await runtime.listTerminals() + graph(runtime) + expect(runtime.capture()).toEqual({ connected: true, incarnationId: NEXT }) + expect(runtime.getPtyLivenessVerdict(PTY)?.status).toBe('live') + }) + + it('keeps an SSH disconnect unverifiable and admissible', () => { + const runtime = new ExitAuthorityRuntime() + const id = 'ssh:target@@terminal' + runtime.registerPty(id, WORKTREE, 'target', { tabId: TAB, leafId: LEAF, incarnationId: FIRST }) + runtime.onPtyExit(id, -1, FIRST) + graph(runtime, id) + expect(runtime.getPtyLivenessVerdict(id)?.status).toBe('unverifiable') + expect(runtime.capture(id).connected).toBe(true) + }) + + it('does not promote an unverified local stop to an exit certificate', () => { + const runtime = new ExitAuthorityRuntime() + runtime.registerPty(PTY, WORKTREE) + runtime.onPtyExit(PTY, -1, FIRST) + runtime.markPtyLivenessUnverifiable(PTY, 'stop unverified') + graph(runtime) + expect(runtime.getPtyLivenessVerdict(PTY)?.status).toBe('unverifiable') + expect(runtime.capture().connected).toBe(true) + }) + + it('bounds certificates for exits whose PTY records are already gone', () => { + const runtime = new ExitAuthorityRuntime() + for (let index = 0; index < 1_000; index++) { + runtime.onPtyExit(`${PTY}-${index}`, 0) + } + expect(runtime.verdictCount).toBe(256) + expect(runtime.getPtyLivenessVerdict(`${PTY}-0`)).toBeNull() + expect(runtime.getPtyLivenessVerdict(`${PTY}-999`)).toEqual({ status: 'exited' }) + }) +}) From f0dfc5de7b8c00c833c36eb83edfedfe95dbaeea Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:31:37 -0700 Subject: [PATCH 071/168] fix(projects): release processed repository scan records (#21022) Co-authored-by: m4air --- .../nested-repo-processed-queue/README.md | 34 +++ .../nested-repo-processed-queue/fix.patch | 28 ++ .../nested-repo-processed-queue/reproduce.cjs | 286 ++++++++++++++++++ .../nested-repo-processed-queue/results.json | 74 +++++ .../nested-repo-discovery-queue.test.ts | 122 ++++++++ .../project-groups/nested-repo-discovery.ts | 10 +- 6 files changed, 552 insertions(+), 2 deletions(-) create mode 100644 docs/audits/nested-repo-processed-queue/README.md create mode 100644 docs/audits/nested-repo-processed-queue/fix.patch create mode 100644 docs/audits/nested-repo-processed-queue/reproduce.cjs create mode 100644 docs/audits/nested-repo-processed-queue/results.json create mode 100644 src/main/project-groups/nested-repo-discovery-queue.test.ts diff --git a/docs/audits/nested-repo-processed-queue/README.md b/docs/audits/nested-repo-processed-queue/README.md new file mode 100644 index 00000000000..c5ef32e0a2b --- /dev/null +++ b/docs/audits/nested-repo-processed-queue/README.md @@ -0,0 +1,34 @@ +# Release completed nested-repository scan records + +`scanNestedRepos` kept every consumed `TraversalFolder` in its breadth-first queue until the scan finished. Those records retained path segments and inherited parsed ignore rules after their directories had been processed. Releasing each consumed slot and occasionally compacting the empty prefix removes that temporary retention while preserving traversal order. + +## Run + +```sh +ORCA_BACKGROUND_LAUNCH=1 node docs/audits/nested-repo-processed-queue/reproduce.cjs +``` + +The runner uses the repository's process launcher to start a Node child with forced GC, a 256 MiB old-space limit and a 15-second timeout. It bundles the actual scan and ignore-rule parser. An observational hook records weak references and scalar queue counts. A finite injected filesystem pauses one directory read; no app window, PTY, SSH connection or native watcher starts. The unused local Git detector is a throwing stub, ensuring the injected filesystem owns every probe. + +The fixture has 96 branches, each with 64 distinct ignore rules and one child directory. It pauses the penultimate child read, leaving one pending directory. Four event-loop-separated GC rounds precede each observation. + +| Observation | Before | Slot-release control | Fixed | +| ------------------------------------------ | ------: | -------------------: | -----: | +| Completed child records surviving GC | 94 / 94 | 0 / 94 | 0 / 94 | +| Their inherited rule arrays surviving GC | 94 / 94 | 0 / 94 | 0 / 94 | +| Observed records surviving scan completion | 0 | 0 | 0 | +| Total directories visited | 193 | 193 | 193 | + +All variants visit the same directories in exactly the same order and return the same empty result. The baseline reverses only `fix.patch` in memory. The diagnostic control adds only consumed-slot clearing to that baseline, without compaction; it isolates the retaining path. The fixed variant executes the current queue implementation. `results.json` includes source hashes, exact queue counts, runtime provenance, process exit and timeout status. + +The narrow source regression suite exercises a wider traversal across repeated compaction, including Windows and SSH POSIX path forms, inherited ignore rules, breadth-first result order, maximum depth, repository caps, cancellation and optional timeout behavior. All 37 discovery, queue and scan-rule tests passed, along with Node typechecking and focused lint checks. Existing discovery tests cover local filesystem and symlink behavior. + +## Reuse and scope + +The change follows the consumed-slot release pattern in `ws-outbound-backpressure-queue.ts` and the amortized prefix compaction pattern in `runtime-rpc-call-queue.ts`. It introduces no new queue abstraction, traversal policy, RPC field or host boundary. + +The baseline source matches `v1.4.198`; the runner verifies this named-tag comparison after normalizing CRLF line endings to LF for Windows checkout portability. This is not an execution of the historical packaged application. + +The IPC route uses this scanner for local and SSH-backed folder selection, with filesystem operations delegated to the selected host. Runtime scan/import routes request a 15-second timeout; IPC forwards options, whose timeout defaults to null. Existing time checks happen between awaited operations and do not cancel a pending read. + +This fix releases completed work. It does not cap the active frontier, directory entry arrays, `.gitignore` size or directory breadth. The original implementation releases its records on scan completion. No heap-byte savings or field-incident attribution is claimed; no affected-host data was used. diff --git a/docs/audits/nested-repo-processed-queue/fix.patch b/docs/audits/nested-repo-processed-queue/fix.patch new file mode 100644 index 00000000000..b79d294abb4 --- /dev/null +++ b/docs/audits/nested-repo-processed-queue/fix.patch @@ -0,0 +1,28 @@ +diff --git a/src/main/project-groups/nested-repo-discovery.ts b/src/main/project-groups/nested-repo-discovery.ts +index 84fddfd116..1f25a85a26 100644 +--- a/src/main/project-groups/nested-repo-discovery.ts ++++ b/src/main/project-groups/nested-repo-discovery.ts +@@ -90,7 +90,7 @@ export async function scanNestedRepos(args: { + return buildResult('non_git_folder') + } + +- const foldersToTraverse: TraversalFolder[] = [ ++ const foldersToTraverse: (TraversalFolder | undefined)[] = [ + { path: args.path, depth: 0, segments: [], ignoreRules: [] } + ] + let nextFolderIndex = 0 +@@ -107,7 +107,13 @@ export async function scanNestedRepos(args: { + if (noteAbort()) { + break + } +- const currentFolder = foldersToTraverse[nextFolderIndex++] ++ const currentFolder = foldersToTraverse[nextFolderIndex++]! ++ // Release processed paths and inherited ignore rules before the next filesystem await. ++ foldersToTraverse[nextFolderIndex - 1] = undefined ++ if (nextFolderIndex >= 64 && nextFolderIndex * 2 >= foldersToTraverse.length) { ++ foldersToTraverse.splice(0, nextFolderIndex) ++ nextFolderIndex = 0 ++ } + if (currentFolder.depth > options.maxDepth) { + continue + } diff --git a/docs/audits/nested-repo-processed-queue/reproduce.cjs b/docs/audits/nested-repo-processed-queue/reproduce.cjs new file mode 100644 index 00000000000..17fd5874c35 --- /dev/null +++ b/docs/audits/nested-repo-processed-queue/reproduce.cjs @@ -0,0 +1,286 @@ +const assert = require('node:assert/strict') +const { createHash } = require('node:crypto') +const { readFileSync, mkdtempSync, rmSync } = require('node:fs') +const { tmpdir } = require('node:os') +const { join, resolve } = require('node:path') +const { build } = require('esbuild') +const { applyPatch, parsePatch, reversePatch } = require('diff') + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1') +} +if (process.argv[2] === '--proof-child' && typeof global.gc !== 'function') { + throw new Error('Child proof requires --expose-gc') +} +const root = resolve(__dirname, '../../..') +const readSource = (path) => readFileSync(path, 'utf8').replace(/\r\n/g, '\n') +const sourcePath = join(root, 'src/main/project-groups/nested-repo-discovery.ts') +const original = readSource(sourcePath) +const patch = parsePatch(readSource(join(__dirname, 'fix.patch'))) +assert.equal(patch.length, 1) +const baseline = applyPatch(original, reversePatch(patch[0])) +assert.notEqual(baseline, false, 'Source changed; review fix.patch') +const hookPoint = ' if (currentFolder.depth > options.maxDepth) {' +assert.equal(original.split(hookPoint).length, 2) +assert.equal(baseline.split(hookPoint).length, 2) +const sha256 = (text) => createHash('sha256').update(text).digest('hex') +const scratch = mkdtempSync(join(tmpdir(), 'orca-nested-queue-proof-')) +const branchCount = 96 +const rulesPerBranch = 64 +const pauseLeaf = branchCount - 2 +const tick = () => new Promise((resolve) => setImmediate(resolve)) +async function gc() { + for (let round = 0; round < 4; round++) { + await tick() + global.gc() + } +} +async function run(mode) { + const output = join(scratch, `${mode}.cjs`) + let source = mode === 'after' ? original : baseline + if (mode === 'clear-consumed-slot') { + const dequeue = ' const currentFolder = foldersToTraverse[nextFolderIndex++]' + assert.equal(source.split(dequeue).length, 2) + source = source.replace( + dequeue, + `${dequeue}\n foldersToTraverse[nextFolderIndex - 1] = undefined` + ) + } + const observedSource = source.replace( + hookPoint, + ` globalThis.__orcaObserveNestedQueue(currentFolder, foldersToTraverse, nextFolderIndex)\n${ + hookPoint + }` + ) + await build({ + entryPoints: [sourcePath], + outfile: output, + bundle: true, + platform: 'node', + format: 'cjs', + logLevel: 'silent', + plugins: [ + { + name: 'observe-actual-nested-queue', + setup(build) { + build.onLoad({ filter: /nested-repo-discovery\.ts$/ }, () => ({ + contents: observedSource, + loader: 'ts', + resolveDir: join(root, 'src/main/project-groups') + })) + build.onResolve({ filter: /^\.\.\/git\/repo$/ }, () => ({ + path: 'inert-git', + namespace: 'proof' + })) + build.onLoad({ filter: /.*/, namespace: 'proof' }, () => ({ + contents: + 'export function isGitRepo() { throw new Error("fixture must use injected filesystem") }', + loader: 'js' + })) + } + } + ] + }) + const { scanNestedRepos } = require(output) + const references = [] + const visits = [] + let pausedState + globalThis.__orcaObserveNestedQueue = (current, queue, head) => { + references.push({ + path: current.path, + record: new WeakRef(current), + inheritedRules: new WeakRef(current.ignoreRules) + }) + if (current.path === `/fixture/b${String(pauseLeaf).padStart(3, '0')}/leaf`) { + pausedState = { + allocatedSlots: queue.length, + consumedSlots: head, + pendingSlots: queue.length - head, + occupiedConsumedSlots: queue.slice(0, head).filter(Boolean).length + } + } + } + let release + const gate = new Promise((resolve) => { + release = resolve + }) + let markPaused + const paused = new Promise((resolve) => { + markPaused = resolve + }) + const resultPromise = scanNestedRepos({ + path: '/fixture', + options: { maxDepth: 3 }, + filesystem: { + async readDirectory(path) { + visits.push(path) + if (path === '/fixture') { + return Array.from({ length: branchCount }, (_, index) => ({ + name: `b${String(index).padStart(3, '0')}`, + isDirectory: true + })) + } + if (!path.endsWith('/leaf')) { + return [ + { name: '.gitignore', isDirectory: false }, + { name: 'leaf', isDirectory: true } + ] + } + if (path === `/fixture/b${String(pauseLeaf).padStart(3, '0')}/leaf`) { + markPaused() + await gate + } + return [] + }, + async readTextFile(path) { + return Array.from( + { length: rulesPerBranch }, + (_, index) => `${path.replaceAll('/', '_')}_unused_${index}` + ).join('\n') + }, + joinPath: (parent, name) => `${parent}/${name}`, + basename: (path) => path.split('/').at(-1), + hasGitMarker: () => false, + isSelectedPathGitRepo: () => false + } + }) + await paused + await gc() + const completedLeaves = references.filter( + ({ path }) => + path.endsWith('/leaf') && path !== `/fixture/b${String(pauseLeaf).padStart(3, '0')}/leaf` + ) + const retained = { + completedLeaves: completedLeaves.length, + retainedCompletedRecords: completedLeaves.filter(({ record }) => record.deref()).length, + retainedCompletedRuleArrays: completedLeaves.filter(({ inheritedRules }) => + inheritedRules.deref() + ).length + } + release() + const result = await resultPromise + delete globalThis.__orcaObserveNestedQueue + await gc() + const afterCompletion = references.filter(({ record }) => record.deref()).length + assert.equal(result.repos.length, 0) + assert.equal(result.stopped, false) + assert.equal(result.timedOut, false) + assert.equal(result.timeoutMs, null) + assert.equal(visits.length, branchCount * 2 + 1) + assert.equal(new Set(visits).size, visits.length) + assert.equal(pausedState.pendingSlots, 1) + assert.equal(retained.completedLeaves, pauseLeaf) + assert.equal(afterCompletion, 0) + delete require.cache[require.resolve(output)] + return { + mode, + pausedState, + retained, + afterCompletion, + totalVisited: visits.length, + visitedOrder: visits + } +} +async function main() { + try { + if (process.argv[2] !== '--proof-child') { + const runnerPath = join(scratch, 'run-process.cjs') + await build({ + entryPoints: [join(root, 'src/shared/child-process/run-process.ts')], + outfile: runnerPath, + bundle: true, + platform: 'node', + format: 'cjs', + logLevel: 'silent' + }) + const { runProcess } = require(runnerPath) + const child = await runProcess({ + program: process.execPath, + args: ['--expose-gc', '--max-old-space-size=256', __filename, '--proof-child'], + cwd: root, + env: process.env, + timeoutMs: 15_000, + maxOutputBytes: 1024 * 1024 + }) + assert.equal(child.timedOut, false, 'Proof timed out') + assert.equal(child.code, 0, child.stderr || child.stdout) + const recorded = JSON.parse(child.stdout) + const historical = await runProcess({ + program: 'git', + args: ['show', 'v1.4.198:src/main/project-groups/nested-repo-discovery.ts'], + cwd: root, + timeoutMs: 5_000, + maxOutputBytes: 256 * 1024 + }) + assert.equal(historical.timedOut, false) + assert.equal(historical.code, 0) + const historicalHash = sha256(historical.stdout.replace(/\r\n/g, '\n')) + assert.equal(historicalHash, recorded.sourceHashes.before) + console.log( + JSON.stringify( + { + ...recorded, + historicalSource: { ref: 'v1.4.198', sha256: historicalHash, equalsBaseline: true }, + process: { + exitCode: child.code, + timedOut: child.timedOut, + timeoutMs: 15_000, + oldSpaceMiB: 256 + } + }, + null, + 2 + ) + ) + delete require.cache[require.resolve(runnerPath)] + return + } + const before = await run('before') + const control = await run('clear-consumed-slot') + const after = await run('after') + assert.equal(before.retained.retainedCompletedRecords, pauseLeaf) + assert.equal(before.retained.retainedCompletedRuleArrays, pauseLeaf) + for (const phase of [control, after]) { + assert.equal(phase.retained.retainedCompletedRecords, 0) + assert.equal(phase.retained.retainedCompletedRuleArrays, 0) + assert.equal(phase.pausedState.occupiedConsumedSlots, 0) + assert.deepEqual(before.visitedOrder, phase.visitedOrder) + } + assert.ok(after.pausedState.allocatedSlots <= 64) + for (const phase of [before, control, after]) { + delete phase.visitedOrder + } + console.log( + JSON.stringify({ + description: + 'Actual nested-repo scan with observational dequeue hook; before reverses fix.patch and control clears only consumed slots.', + sourceHashes: { + normalization: 'UTF-8 source with CRLF line endings normalized to LF', + before: sha256(baseline), + after: sha256(original), + rules: sha256( + readSource(join(root, 'src/main/project-groups/nested-repo-scan-rules.ts')) + ), + regression: sha256( + readSource(join(root, 'src/main/project-groups/nested-repo-discovery-queue.test.ts')) + ), + runner: sha256(readSource(__filename)) + }, + nodeVersion: process.version, + branchCount, + rulesPerBranch, + before, + control, + after, + passed: true + }) + ) + } finally { + delete globalThis.__orcaObserveNestedQueue + rmSync(scratch, { recursive: true, force: true }) + } +} +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/docs/audits/nested-repo-processed-queue/results.json b/docs/audits/nested-repo-processed-queue/results.json new file mode 100644 index 00000000000..0fca2b45cee --- /dev/null +++ b/docs/audits/nested-repo-processed-queue/results.json @@ -0,0 +1,74 @@ +{ + "description": "Actual nested-repo scan with observational dequeue hook; before reverses fix.patch and control clears only consumed slots.", + "sourceHashes": { + "normalization": "UTF-8 source with CRLF line endings normalized to LF", + "before": "0a0952888db648a77776becfa9fcfc783d0b905ce096e70311d532ea2675065e", + "after": "8517a2bc2220e5fb3e96f063e1714911235ab040ee21487ca537d5c34d3cc81d", + "rules": "4613599bf5382edd86ae33bc84548f247018f26acbf2f7db072d847fa5533660", + "regression": "5e343d4da0627458ebd15c5c619b861c07388830b2a65aef76c9fb0dcd9d4c8d", + "runner": "2bafd8e2de95a86bcefdef484f63002cb0f59428b5359a3b9b9f16b8942ac11f" + }, + "nodeVersion": "v26.6.0", + "branchCount": 96, + "rulesPerBranch": 64, + "before": { + "mode": "before", + "pausedState": { + "allocatedSlots": 193, + "consumedSlots": 192, + "pendingSlots": 1, + "occupiedConsumedSlots": 192 + }, + "retained": { + "completedLeaves": 94, + "retainedCompletedRecords": 94, + "retainedCompletedRuleArrays": 94 + }, + "afterCompletion": 0, + "totalVisited": 193 + }, + "control": { + "mode": "clear-consumed-slot", + "pausedState": { + "allocatedSlots": 193, + "consumedSlots": 192, + "pendingSlots": 1, + "occupiedConsumedSlots": 0 + }, + "retained": { + "completedLeaves": 94, + "retainedCompletedRecords": 0, + "retainedCompletedRuleArrays": 0 + }, + "afterCompletion": 0, + "totalVisited": 193 + }, + "after": { + "mode": "after", + "pausedState": { + "allocatedSlots": 34, + "consumedSlots": 33, + "pendingSlots": 1, + "occupiedConsumedSlots": 0 + }, + "retained": { + "completedLeaves": 94, + "retainedCompletedRecords": 0, + "retainedCompletedRuleArrays": 0 + }, + "afterCompletion": 0, + "totalVisited": 193 + }, + "passed": true, + "historicalSource": { + "ref": "v1.4.198", + "sha256": "0a0952888db648a77776becfa9fcfc783d0b905ce096e70311d532ea2675065e", + "equalsBaseline": true + }, + "process": { + "exitCode": 0, + "timedOut": false, + "timeoutMs": 15000, + "oldSpaceMiB": 256 + } +} diff --git a/src/main/project-groups/nested-repo-discovery-queue.test.ts b/src/main/project-groups/nested-repo-discovery-queue.test.ts new file mode 100644 index 00000000000..16eb82fdd54 --- /dev/null +++ b/src/main/project-groups/nested-repo-discovery-queue.test.ts @@ -0,0 +1,122 @@ +import { posix, win32 } from 'node:path' +import { afterEach, expect, it, vi } from 'vitest' +import { scanNestedRepos } from './nested-repo-discovery' + +const branchNames = Array.from( + { length: 160 }, + (_, index) => `branch-${String(index).padStart(3, '0')}` +) +afterEach(() => vi.restoreAllMocks()) + +function fixture(paths: typeof posix, onRead: (count: number) => void = () => {}) { + const root = paths.resolve('/workspace') + const visits: string[] = [] + const branches = branchNames.map((name) => paths.join(root, name)) + const descendants = branches.map((path) => paths.join(path, 'deeper')) + const repositories = descendants.map((path) => paths.join(path, 'repository')) + return { + root, + visits, + branches, + descendants, + repositories, + filesystem: { + readDirectory: async (path: string) => { + visits.push(path) + onRead(visits.length) + const names = + path === root + ? branchNames.toReversed() + : paths.basename(path) === 'deeper' + ? ['repository'] + : ['ignored', 'deeper', '.gitignore'] + return names.map((name) => ({ name, isDirectory: name !== '.gitignore' })) + }, + readTextFile: async () => 'ignored/', + joinPath: paths.join, + basename: paths.basename, + hasGitMarker: (path: string) => paths.basename(path) === 'repository', + isSelectedPathGitRepo: () => false + } + } +} + +it.each([ + ['local Windows paths', win32], + ['SSH POSIX paths', posix] +] as const)('preserves broad BFS order and inherited ignores with %s', async (_label, paths) => { + const f = fixture(paths) + const result = await scanNestedRepos({ + path: f.root, + options: { maxRepos: 500 }, + filesystem: f.filesystem + }) + expect(f.visits).toEqual([f.root, ...f.branches, ...f.descendants]) + expect(result.repos.map(({ path }) => path)).toEqual(f.repositories) + expect(result.repos.every(({ depth }) => depth === 3)).toBe(true) + expect(result).toMatchObject({ + truncated: false, + stopped: false, + timedOut: false, + timeoutMs: null + }) +}) + +it('preserves max depth and result caps during broad traversal', async () => { + const depth = fixture(posix) + const boundedDepth = await scanNestedRepos({ + path: depth.root, + options: { maxDepth: 1 }, + filesystem: depth.filesystem + }) + expect(depth.visits).toEqual([depth.root, ...depth.branches]) + expect(boundedDepth.repos).toEqual([]) + const capped = fixture(posix) + const boundedResults = await scanNestedRepos({ + path: capped.root, + options: { maxRepos: 7 }, + filesystem: capped.filesystem + }) + expect(boundedResults.repos.map(({ path }) => path)).toEqual(capped.repositories.slice(0, 7)) + expect(boundedResults.truncated).toBe(true) +}) + +it('honors abort after a broad prefix has been consumed', async () => { + const controller = new AbortController() + const f = fixture(posix, (count) => { + if (count === 200) { + controller.abort() + } + }) + const result = await scanNestedRepos({ + path: f.root, + signal: controller.signal, + options: { maxRepos: 500 }, + filesystem: f.filesystem + }) + expect(f.visits).toHaveLength(200) + expect(result.repos.map(({ path }) => path)).toEqual(f.repositories.slice(0, 38)) + expect(result).toMatchObject({ stopped: true, timedOut: false }) +}) + +it.each([null, 500])( + 'preserves optional timeout=%s after consuming a broad prefix', + async (timeoutMs) => { + let now = 0 + vi.spyOn(Date, 'now').mockImplementation(() => now) + const f = fixture(posix, (count) => { + if (count === 200) { + now = 1_000 + } + }) + const result = await scanNestedRepos({ + path: f.root, + options: { maxRepos: 500, timeoutMs }, + filesystem: f.filesystem + }) + expect(result.repos.map(({ path }) => path)).toEqual( + timeoutMs === null ? f.repositories : f.repositories.slice(0, 38) + ) + expect(result).toMatchObject({ timedOut: timeoutMs !== null, timeoutMs, stopped: false }) + } +) diff --git a/src/main/project-groups/nested-repo-discovery.ts b/src/main/project-groups/nested-repo-discovery.ts index 84fddfd1167..1f25a85a26f 100644 --- a/src/main/project-groups/nested-repo-discovery.ts +++ b/src/main/project-groups/nested-repo-discovery.ts @@ -90,7 +90,7 @@ export async function scanNestedRepos(args: { return buildResult('non_git_folder') } - const foldersToTraverse: TraversalFolder[] = [ + const foldersToTraverse: (TraversalFolder | undefined)[] = [ { path: args.path, depth: 0, segments: [], ignoreRules: [] } ] let nextFolderIndex = 0 @@ -107,7 +107,13 @@ export async function scanNestedRepos(args: { if (noteAbort()) { break } - const currentFolder = foldersToTraverse[nextFolderIndex++] + const currentFolder = foldersToTraverse[nextFolderIndex++]! + // Release processed paths and inherited ignore rules before the next filesystem await. + foldersToTraverse[nextFolderIndex - 1] = undefined + if (nextFolderIndex >= 64 && nextFolderIndex * 2 >= foldersToTraverse.length) { + foldersToTraverse.splice(0, nextFolderIndex) + nextFolderIndex = 0 + } if (currentFolder.depth > options.maxDepth) { continue } From c3c051dfa62860e0eec6dfc37b81106eba394125 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:32:45 -0700 Subject: [PATCH 072/168] Release provider children after structured session holds disappear (#20978) * fix(chat): release provider children after lost resume holds * test: load audit fixtures as modules and verify combined mobile payload --------- Co-authored-by: m4air --- .../structured-hold-retention/README.md | 51 ++++ .../structured-hold-retention/reproduce.mjs | 129 ++++++++ .../structured-hold-retention/results.json | 18 ++ ...red-agent-session-hold-resume-race.test.ts | 289 ++++++++++++++++++ .../structured-agent-session-holders.ts | 28 +- .../structured-agent-session-holds.ts | 17 +- .../structured-agent-session-hold.test.ts | 101 ++++++ .../methods/structured-agent-session-hold.ts | 4 +- ...ss-version-agent-session-wire.unit.test.ts | 20 +- 9 files changed, 631 insertions(+), 26 deletions(-) create mode 100644 docs/audits/structured-hold-retention/README.md create mode 100644 docs/audits/structured-hold-retention/reproduce.mjs create mode 100644 docs/audits/structured-hold-retention/results.json create mode 100644 src/main/native-chat/agent-session-wire/structured-agent-session-hold-resume-race.test.ts diff --git a/docs/audits/structured-hold-retention/README.md b/docs/audits/structured-hold-retention/README.md new file mode 100644 index 00000000000..c0076813f6c --- /dev/null +++ b/docs/audits/structured-hold-retention/README.md @@ -0,0 +1,51 @@ +# Structured session hold lost during resume + +Run from the repository root: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node docs/audits/structured-hold-retention/reproduce.mjs +``` + +The script bundles the actual `StructuredAgentSessionHolds` implementation into temporary CommonJS +modules, loads them normally, and removes their files and module-cache entries. It runs the code +once without the post-resume holder check and once with the current source. It uses a deferred +provider acquisition, an isolated fake child, and a 5 ms release grace. It launches no application, +provider, or terminal and reads no user profile. + +The last surface releases its hold while acquisition is pending. At that point the session has no +provider child, so `release()` cannot arm the release clock. Before the fix, acquisition completes +with a child, zero holders, and no scheduled eviction. With the fix, successful acquisition checks +for surviving holders and schedules the existing release clock. The recorded child is released once. + +The RPC path registers connection cleanup before awaiting `host.hold()` in +`src/main/runtime/rpc/methods/structured-agent-session-hold.ts`. Runtime socket close calls +`cleanupSubscriptionsForConnection()` in `runtime-rpc/runtime-rpc-lifecycle.ts`. That supplies the +production release-during-acquisition ordering reproduced here. + +## Ownership limits + +- This proves a lifecycle race, not that it caused any particular OOM report. No process RSS was + measured. It applies to structured sessions acquiring a provider child, not ordinary PTY tabs. +- The release clock preserves its 15-second production grace, waits while a turn is active, and + cancels when a new holder arrives. Acquisition failures retain their existing handling. +- Disposal prevents late acquisition or release callbacks from restarting the clock. Host teardown + owns cleanup after disposal. The host's broader pre-attach shutdown admission is outside this fix. +- A restored childless journal is not necessarily abandoned. Startup selects persisted visible + tabs; `host.sessions` supplies `listSessionTabs()`, and childless sessions can retain live TUI + owners. `host.close()` closes that TUI owner before removing the journal. This fix neither evicts + childless history nor infers process exit from transport loss. +- Execution remains on the owning runtime, with no wire or SSH routing changes. + +Targeted regressions live in +`src/main/native-chat/agent-session-wire/structured-agent-session-hold-resume-race.test.ts`. +They cover last-holder loss, active turns, new holders, reconnection, failed acquisition, explicit +close, and disposal. + +Same-ID replacement is fenced at both ownership layers. Holder entries receive a new incarnation +after release and re-add, so an old failed acquisition cannot remove a replacement. The RPC uses +the subscription registry's `releaseIfCurrent()` cleanup, so its failure cannot unregister the +replacement's connection cleanup. Duplicate adds remain one holder. Class tests and real host/RPC +tests cover the old failure arriving before and after replacement success; disconnect still releases +the replacement normally. They also cover the reverse outcome: an old acquisition succeeds and the +replacement refuses a stale fence. Its last-holder rollback starts the same turn-aware release clock +for the acquired child. No RPC fields or published frame shapes change. diff --git a/docs/audits/structured-hold-retention/reproduce.mjs b/docs/audits/structured-hold-retention/reproduce.mjs new file mode 100644 index 00000000000..b1e6ed5c32c --- /dev/null +++ b/docs/audits/structured-hold-retention/reproduce.mjs @@ -0,0 +1,129 @@ +import { createHash } from 'node:crypto' +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { createRequire } from 'node:module' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { build } from 'esbuild' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1.') +} + +const root = fileURLToPath(new URL('../../../', import.meta.url)) +const sourcePath = fileURLToPath( + new URL( + '../../../src/main/native-chat/agent-session-wire/structured-agent-session-holds.ts', + import.meta.url + ) +) +const source = await readFile(sourcePath, 'utf8') +const postResumeCheck = + ' // The last surface can disconnect before acquisition makes a child available to release.\n' + + ' if (!this.disposed && !this.holders.isHeld(sessionId)) {\n' + + ' this.clock.arm(sessionId)\n' + + ' }\n' +if (!source.includes(postResumeCheck)) { + throw new Error('Source changed: review the before-fix transform before running this proof.') +} + +async function loadHolds(withPostResumeCheck) { + const result = await build({ + absWorkingDir: root, + entryPoints: [sourcePath], + bundle: true, + platform: 'node', + format: 'cjs', + write: false, + logLevel: 'silent', + plugins: [ + { + name: 'compare-post-resume-holder-check', + setup(plugin) { + plugin.onLoad({ filter: /structured-agent-session-holds\.ts$/ }, () => ({ + contents: withPostResumeCheck ? source : source.replace(postResumeCheck, ''), + loader: 'ts' + })) + } + } + ] + }) + const scratch = await mkdtemp(join(tmpdir(), 'orca-structured-hold-proof-')) + const require = createRequire(import.meta.url) + let moduleId + try { + const bundlePath = join(scratch, 'holds.cjs') + await writeFile(bundlePath, result.outputFiles[0].text) + moduleId = require.resolve(bundlePath) + return require(moduleId).StructuredAgentSessionHolds + } finally { + if (moduleId) { + delete require.cache[moduleId] + } + await rm(scratch, { recursive: true, force: true }) + } +} + +async function reproduce(Holds) { + const gate = Promise.withResolvers() + let child = false + let evictions = 0 + const holds = new Holds({ + resume: async () => { + await gate.promise + child = true + }, + hasProviderChild: () => child, + isTurnActive: () => false, + evict: async () => { + evictions += 1 + child = false + }, + graceMs: 5 + }) + try { + const acquiring = holds.hold('restored-session', 'connection:surface') + holds.release('restored-session', 'connection:surface') + gate.resolve() + await acquiring + const releasePendingAfterAcquisition = holds.isReleasePending('restored-session') + await new Promise((resolve) => setTimeout(resolve, 30)) + return { + child, + held: holds.isHeld('restored-session'), + releasePendingAfterAcquisition, + evictions + } + } finally { + holds.dispose() + } +} + +const before = await reproduce(await loadHolds(false)) +const after = await reproduce(await loadHolds(true)) +const passed = + before.child && + !before.held && + !before.releasePendingAfterAcquisition && + before.evictions === 0 && + !after.child && + !after.held && + after.releasePendingAfterAcquisition && + after.evictions === 1 +console.log( + JSON.stringify( + { + source: 'src/main/native-chat/agent-session-wire/structured-agent-session-holds.ts', + sourceSha256: createHash('sha256').update(source).digest('hex'), + comparison: 'same source, before omits only the post-resume holder check', + before, + after, + passed + }, + null, + 2 + ) +) +if (!passed) { + process.exitCode = 1 +} diff --git a/docs/audits/structured-hold-retention/results.json b/docs/audits/structured-hold-retention/results.json new file mode 100644 index 00000000000..6019410f918 --- /dev/null +++ b/docs/audits/structured-hold-retention/results.json @@ -0,0 +1,18 @@ +{ + "source": "src/main/native-chat/agent-session-wire/structured-agent-session-holds.ts", + "sourceSha256": "c95e27518d6cb09f1c97e5ff18bbb9fc5b15c7680d4a99e90cc14353d602230f", + "comparison": "same source, before omits only the post-resume holder check", + "before": { + "child": true, + "held": false, + "releasePendingAfterAcquisition": false, + "evictions": 0 + }, + "after": { + "child": false, + "held": false, + "releasePendingAfterAcquisition": true, + "evictions": 1 + }, + "passed": true +} diff --git a/src/main/native-chat/agent-session-wire/structured-agent-session-hold-resume-race.test.ts b/src/main/native-chat/agent-session-wire/structured-agent-session-hold-resume-race.test.ts new file mode 100644 index 00000000000..bd3a840d9ad --- /dev/null +++ b/src/main/native-chat/agent-session-wire/structured-agent-session-hold-resume-race.test.ts @@ -0,0 +1,289 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { StructuredAgentSessionHolds } from './structured-agent-session-holds' + +const GRACE_MS = 15_000 +const pendingHolds: StructuredAgentSessionHolds[] = [] + +function resumeHarness() { + const resumeGate = Promise.withResolvers() + let child = false + let turnActive = false + const evict = vi.fn(async () => { + child = false + }) + const holds = new StructuredAgentSessionHolds({ + resume: async () => { + await resumeGate.promise + child = true + }, + hasProviderChild: () => child, + isTurnActive: () => turnActive, + evict, + graceMs: GRACE_MS + }) + pendingHolds.push(holds) + return { + holds, + resumeGate, + evict, + hasChild: () => child, + setTurnActive: (value: boolean) => { + turnActive = value + } + } +} + +beforeEach(() => vi.useFakeTimers()) +afterEach(() => { + for (const holds of pendingHolds.splice(0)) { + holds.dispose() + } + vi.useRealTimers() +}) + +describe('a surface leaving while its structured session resumes', () => { + it('releases the acquired child after the last surface disconnects during resume', async () => { + const { holds, resumeGate, evict, hasChild } = resumeHarness() + const hold = holds.hold('session-1', 'connection-1:chat') + + holds.release('session-1', 'connection-1:chat') + expect(holds.isReleasePending('session-1')).toBe(false) + resumeGate.resolve() + await hold + + expect(hasChild()).toBe(true) + expect(holds.isHeld('session-1')).toBe(false) + expect(holds.isReleasePending('session-1')).toBe(true) + await vi.advanceTimersByTimeAsync(GRACE_MS - 1) + expect(evict).not.toHaveBeenCalled() + await vi.advanceTimersByTimeAsync(1) + expect(evict).toHaveBeenCalledExactlyOnceWith('session-1') + expect(hasChild()).toBe(false) + }) + + it('waits for an active turn before releasing the late child', async () => { + const { holds, resumeGate, evict, setTurnActive } = resumeHarness() + const hold = holds.hold('session-1', 'connection-1:chat') + holds.release('session-1', 'connection-1:chat') + setTurnActive(true) + resumeGate.resolve() + await hold + + await vi.advanceTimersByTimeAsync(GRACE_MS * 2) + expect(evict).not.toHaveBeenCalled() + expect(holds.isReleasePending('session-1')).toBe(true) + + setTurnActive(false) + await vi.advanceTimersByTimeAsync(GRACE_MS) + expect(evict).toHaveBeenCalledExactlyOnceWith('session-1') + }) + + it.each([false, true])('preserves an arriving holder with resume=%s', async (resume) => { + const { holds, resumeGate, evict } = resumeHarness() + const first = holds.hold('session-1', 'connection-1:chat') + holds.release('session-1', 'connection-1:chat') + const replacement = holds.hold('session-1', 'connection-2:chat', { resume }) + resumeGate.resolve() + await Promise.all([first, replacement]) + + await vi.advanceTimersByTimeAsync(GRACE_MS * 2) + expect(holds.isHeld('session-1')).toBe(true) + expect(holds.isReleasePending('session-1')).toBe(false) + expect(evict).not.toHaveBeenCalled() + + holds.release('session-1', 'connection-2:chat') + await vi.advanceTimersByTimeAsync(GRACE_MS) + expect(evict).toHaveBeenCalledExactlyOnceWith('session-1') + }) + + it('cancels the late-child release when a surface reconnects during grace', async () => { + const { holds, resumeGate, evict } = resumeHarness() + const hold = holds.hold('session-1', 'connection-1:chat') + holds.release('session-1', 'connection-1:chat') + resumeGate.resolve() + await hold + expect(holds.isReleasePending('session-1')).toBe(true) + + await holds.hold('session-1', 'connection-2:chat') + await vi.advanceTimersByTimeAsync(GRACE_MS * 2) + expect(holds.isReleasePending('session-1')).toBe(false) + expect(evict).not.toHaveBeenCalled() + }) + + it('preserves a failed resume without scheduling eviction', async () => { + const { holds, resumeGate, evict, hasChild } = resumeHarness() + const failure = new Error('provider acquisition failed') + const hold = holds.hold('session-1', 'connection-1:chat') + const rejected = expect(hold).rejects.toBe(failure) + holds.release('session-1', 'connection-1:chat') + resumeGate.reject(failure) + await rejected + + await vi.advanceTimersByTimeAsync(GRACE_MS * 2) + expect(hasChild()).toBe(false) + expect(holds.isHeld('session-1')).toBe(false) + expect(holds.isReleasePending('session-1')).toBe(false) + expect(evict).not.toHaveBeenCalled() + }) + + it('leaves late acquisition cleanup to host teardown after disposal', async () => { + const { holds, resumeGate, evict } = resumeHarness() + const hold = holds.hold('session-1', 'connection-1:chat') + holds.release('session-1', 'connection-1:chat') + holds.dispose() + resumeGate.resolve() + await hold + + await vi.advanceTimersByTimeAsync(GRACE_MS * 2) + expect(holds.isReleasePending('session-1')).toBe(false) + expect(evict).not.toHaveBeenCalled() + }) + + it('does not restart release timers when a surface leaves after disposal', async () => { + const { holds, resumeGate, evict } = resumeHarness() + const hold = holds.hold('session-1', 'connection-1:chat') + resumeGate.resolve() + await hold + holds.dispose() + holds.release('session-1', 'connection-1:chat') + + await vi.advanceTimersByTimeAsync(GRACE_MS * 2) + expect(holds.isHeld('session-1')).toBe(false) + expect(holds.isReleasePending('session-1')).toBe(false) + expect(evict).not.toHaveBeenCalled() + }) + + it('releases a late child acquired after explicit close forgot its holders', async () => { + const { holds, resumeGate, evict } = resumeHarness() + const hold = holds.hold('session-1', 'connection-1:chat') + holds.forget('session-1') + resumeGate.resolve() + await hold + + await vi.advanceTimersByTimeAsync(GRACE_MS) + expect(holds.isHeld('session-1')).toBe(false) + expect(evict).toHaveBeenCalledExactlyOnceWith('session-1') + }) + + it.each([false, true])( + 'keeps a reused holder when old resume fails (replacement finished=%s)', + async (replacementFinished) => { + const firstGate = Promise.withResolvers() + const replacementGate = Promise.withResolvers() + let child = false + const resume = vi + .fn() + .mockImplementationOnce(() => firstGate.promise) + .mockImplementationOnce(async () => { + await replacementGate.promise + child = true + }) + const evict = vi.fn(async () => {}) + const holds = new StructuredAgentSessionHolds({ + resume, + hasProviderChild: () => child, + isTurnActive: () => false, + evict, + graceMs: GRACE_MS + }) + pendingHolds.push(holds) + const first = holds.hold('session-1', 'same-holder') + const rejected = expect(first).rejects.toThrow('old acquisition failed') + holds.release('session-1', 'same-holder') + const replacement = holds.hold('session-1', 'same-holder') + if (replacementFinished) { + replacementGate.resolve() + await replacement + } + + firstGate.reject(new Error('old acquisition failed')) + await rejected + expect(holds.isHeld('session-1')).toBe(true) + replacementGate.resolve() + await replacement + await vi.advanceTimersByTimeAsync(GRACE_MS * 2) + expect(evict).not.toHaveBeenCalled() + + holds.release('session-1', 'same-holder') + await vi.advanceTimersByTimeAsync(GRACE_MS) + expect(evict).toHaveBeenCalledExactlyOnceWith('session-1') + } + ) + + it('removes a failed replacement while the released old hold is still pending', async () => { + const firstGate = Promise.withResolvers() + const resume = vi + .fn() + .mockImplementationOnce(() => firstGate.promise) + .mockRejectedValueOnce(new Error('replacement acquisition failed')) + const holds = new StructuredAgentSessionHolds({ + resume, + hasProviderChild: () => false, + isTurnActive: () => false, + evict: async () => {}, + graceMs: GRACE_MS + }) + pendingHolds.push(holds) + const first = holds.hold('session-1', 'same-holder') + const rejected = expect(first).rejects.toThrow('old acquisition failed') + holds.release('session-1', 'same-holder') + + await expect(holds.hold('session-1', 'same-holder')).rejects.toThrow( + 'replacement acquisition failed' + ) + expect(holds.isHeld('session-1')).toBe(false) + expect(holds.isReleasePending('session-1')).toBe(false) + + firstGate.reject(new Error('old acquisition failed')) + await rejected + expect(holds.isHeld('session-1')).toBe(false) + }) + + it.each(['old-holder', 'different-holder'])( + 'releases the old acquisition after replacement %s fails, once its turn finishes', + async (replacementHolder) => { + const firstGate = Promise.withResolvers() + const replacementGate = Promise.withResolvers() + let child = false + let turnActive = true + const resume = vi + .fn() + .mockImplementationOnce(async () => { + await firstGate.promise + child = true + }) + .mockImplementationOnce(() => replacementGate.promise) + const evict = vi.fn(async () => { + child = false + }) + const holds = new StructuredAgentSessionHolds({ + resume, + hasProviderChild: () => child, + isTurnActive: () => turnActive, + evict, + graceMs: GRACE_MS + }) + pendingHolds.push(holds) + const first = holds.hold('session-1', 'old-holder') + holds.release('session-1', 'old-holder') + const replacement = holds.hold('session-1', replacementHolder) + const rejected = expect(replacement).rejects.toThrow('replacement acquisition failed') + firstGate.resolve() + await first + expect(holds.isReleasePending('session-1')).toBe(false) + + replacementGate.reject(new Error('replacement acquisition failed')) + await rejected + expect(holds.isHeld('session-1')).toBe(false) + expect(holds.isReleasePending('session-1')).toBe(true) + await vi.advanceTimersByTimeAsync(GRACE_MS) + expect(evict).not.toHaveBeenCalled() + expect(child).toBe(true) + + turnActive = false + await vi.advanceTimersByTimeAsync(GRACE_MS) + expect(evict).toHaveBeenCalledExactlyOnceWith('session-1') + expect(child).toBe(false) + } + ) +}) diff --git a/src/main/native-chat/agent-session-wire/structured-agent-session-holders.ts b/src/main/native-chat/agent-session-wire/structured-agent-session-holders.ts index ed0451efb79..531717168b1 100644 --- a/src/main/native-chat/agent-session-wire/structured-agent-session-holders.ts +++ b/src/main/native-chat/agent-session-wire/structured-agent-session-holders.ts @@ -6,23 +6,35 @@ // still looking at, and a lost one leaks the child forever. A set answers both idempotently, // because it records WHICH surface holds the session, not how many do. +type Holder = { resumeCapable: boolean; incarnation: symbol } + export class StructuredAgentSessionHolders { - private readonly bySession = new Map>() + private readonly bySession = new Map>() /** True when the session gained its FIRST holder — the edge that ends a pending release. */ add(sessionId: string, holderId: string, resumeCapable = true): boolean { const holders = this.bySession.get(sessionId) if (!holders) { - this.bySession.set(sessionId, new Map([[holderId, resumeCapable]])) + this.bySession.set(sessionId, new Map([[holderId, { resumeCapable, incarnation: Symbol() }]])) return true } - holders.set(holderId, (holders.get(holderId) ?? false) || resumeCapable) + const previous = holders.get(holderId) + holders.set(holderId, { + resumeCapable: (previous?.resumeCapable ?? false) || resumeCapable, + incarnation: previous?.incarnation ?? Symbol() + }) return false } /** True when the session lost its LAST holder — the edge that starts one. */ - remove(sessionId: string, holderId: string): boolean { + remove(sessionId: string, holderId: string, expectedIncarnation?: symbol): boolean { const holders = this.bySession.get(sessionId) + if ( + expectedIncarnation !== undefined && + holders?.get(holderId)?.incarnation !== expectedIncarnation + ) { + return false + } if (!holders?.delete(holderId) || holders.size > 0) { return false } @@ -38,12 +50,18 @@ export class StructuredAgentSessionHolders { return this.bySession.get(sessionId)?.has(holderId) ?? false } + incarnation(sessionId: string, holderId: string): symbol | undefined { + return this.bySession.get(sessionId)?.get(holderId)?.incarnation + } + holderIds(sessionId: string): string[] { return [...(this.bySession.get(sessionId)?.keys() ?? [])] } hasResumeCapableHolder(sessionId: string): boolean { - return [...(this.bySession.get(sessionId)?.values() ?? [])].some(Boolean) + return [...(this.bySession.get(sessionId)?.values() ?? [])].some( + (holder) => holder.resumeCapable + ) } /** Drops every holder of one session without evaluating the edge, for a session that is gone. */ diff --git a/src/main/native-chat/agent-session-wire/structured-agent-session-holds.ts b/src/main/native-chat/agent-session-wire/structured-agent-session-holds.ts index 6afc8abc052..027c2d54772 100644 --- a/src/main/native-chat/agent-session-wire/structured-agent-session-holds.ts +++ b/src/main/native-chat/agent-session-wire/structured-agent-session-holds.ts @@ -36,6 +36,7 @@ export type StructuredAgentSessionHoldOptions = { export class StructuredAgentSessionHolds { private readonly holders = new StructuredAgentSessionHolders() private readonly clock: StructuredAgentSessionReleaseClock + private disposed = false constructor(private readonly deps: StructuredAgentSessionHoldsDeps) { const clockDeps: StructuredAgentSessionReleaseClockDeps = { @@ -55,6 +56,7 @@ export class StructuredAgentSessionHolds { ): Promise { const alreadyHeld = this.holders.has(sessionId, holderId) this.holders.add(sessionId, holderId, options.resume !== false) + const incarnation = this.holders.incarnation(sessionId, holderId) // Unconditional, not only on the first-holder edge: a second surface arriving during the grace // window must cancel the pending release too. this.clock.cancel(sessionId) @@ -66,19 +68,23 @@ export class StructuredAgentSessionHolds { if (!this.deps.hasProviderChild(sessionId)) { throw new Error('agent_session_ownership_unknown') } + // The last surface can disconnect before acquisition makes a child available to release. + if (!this.disposed && !this.holders.isHeld(sessionId)) { + this.clock.arm(sessionId) + } } catch (error) { - if (!alreadyHeld) { - this.holders.remove(sessionId, holderId) + if (!alreadyHeld && incarnation !== undefined) { + this.release(sessionId, holderId, incarnation) } throw error } } - release(sessionId: string, holderId: string): void { - if (!this.holders.remove(sessionId, holderId)) { + release(sessionId: string, holderId: string, expectedIncarnation?: symbol): void { + if (!this.holders.remove(sessionId, holderId, expectedIncarnation)) { return } - if (this.deps.hasProviderChild(sessionId)) { + if (!this.disposed && this.deps.hasProviderChild(sessionId)) { this.clock.arm(sessionId) } } @@ -102,6 +108,7 @@ export class StructuredAgentSessionHolds { } dispose(): void { + this.disposed = true this.clock.dispose() } } diff --git a/src/main/runtime/rpc/methods/structured-agent-session-hold.test.ts b/src/main/runtime/rpc/methods/structured-agent-session-hold.test.ts index 4e6dfdf45bf..da34108604a 100644 --- a/src/main/runtime/rpc/methods/structured-agent-session-hold.test.ts +++ b/src/main/runtime/rpc/methods/structured-agent-session-hold.test.ts @@ -162,6 +162,107 @@ describe('a client that holds a session', () => { }) describe('a client that disappears without cleanup', () => { + it('releases a late child after its same-ID replacement refuses the stale fence', async () => { + await host.close(SESSION) + await host.restoreReadableSessions() + closeSession.mockClear() + const firstEntered = Promise.withResolvers() + const firstGate = Promise.withResolvers() + const replacementEntered = Promise.withResolvers() + const replacementGate = Promise.withResolvers() + const attach = host.attach.bind(host) + const attachSpy = vi + .spyOn(host, 'attach') + .mockImplementationOnce(async (...args) => { + firstEntered.resolve() + await firstGate.promise + return attach(...args) + }) + .mockImplementationOnce(async (...args) => { + replacementEntered.resolve() + await replacementGate.promise + return attach(...args) + }) + try { + const params = { sessionId: SESSION, holderId: 'same-chat' } + const first = call('agentSession.hold', params) + await firstEntered.promise + const replacement = call('agentSession.hold', params) + await replacementEntered.promise + firstGate.resolve() + expect(await first).toMatchObject({ ok: true }) + expect(host.isHeld(SESSION)).toBe(true) + expect(closeSession).not.toHaveBeenCalled() + + replacementGate.resolve() + expect(await replacement).toMatchObject({ + ok: false, + error: { code: 'agent_session_checkpoint_stale' } + }) + expect(host.isHeld(SESSION)).toBe(false) + await vi.waitFor(() => expect(host.hasSession(SESSION)).toBe(false)) + expect(closeSession).toHaveBeenCalledExactlyOnceWith(SESSION) + } finally { + firstGate.resolve() + replacementGate.resolve() + attachSpy.mockRestore() + } + }) + + it.each([false, true])( + 'keeps replacement hold and cleanup after an old request fails (replacement finished=%s)', + async (replacementFinished) => { + await host.close(SESSION) + await host.restoreReadableSessions() + closeSession.mockClear() + const firstEntered = Promise.withResolvers() + const firstGate = Promise.withResolvers() + const replacementEntered = Promise.withResolvers() + const replacementGate = Promise.withResolvers() + const attach = host.attach.bind(host) + const attachSpy = vi + .spyOn(host, 'attach') + .mockImplementationOnce(async () => { + firstEntered.resolve() + await firstGate.promise + throw new Error('old acquisition failed') + }) + .mockImplementationOnce(async (...args) => { + replacementEntered.resolve() + await replacementGate.promise + return attach(...args) + }) + try { + const params = { sessionId: SESSION, holderId: 'same-chat' } + const first = call('agentSession.hold', params) + await firstEntered.promise + const replacement = call('agentSession.hold', params) + await replacementEntered.promise + if (replacementFinished) { + replacementGate.resolve() + expect(await replacement).toMatchObject({ ok: true }) + } + + firstGate.resolve() + expect(await first).toMatchObject({ ok: false }) + expect(host.isHeld(SESSION)).toBe(true) + replacementGate.resolve() + expect(await replacement).toMatchObject({ ok: true }) + await new Promise((resolve) => setTimeout(resolve, GRACE_MS * 4)) + expect(host.hasSession(SESSION)).toBe(true) + expect(closeSession).not.toHaveBeenCalled() + + runtime.cleanupSubscriptionsForConnection(CONNECTION) + await vi.waitFor(() => expect(host.hasSession(SESSION)).toBe(false)) + expect(closeSession).toHaveBeenCalledExactlyOnceWith(SESSION) + } finally { + firstGate.resolve() + replacementGate.resolve() + attachSpy.mockRestore() + } + } + ) + it('still releases the session when its transport closes', async () => { await call('agentSession.hold', { sessionId: SESSION, holderId: 'chat-1' }) diff --git a/src/main/runtime/rpc/methods/structured-agent-session-hold.ts b/src/main/runtime/rpc/methods/structured-agent-session-hold.ts index 18fb600a949..54753fcb889 100644 --- a/src/main/runtime/rpc/methods/structured-agent-session-hold.ts +++ b/src/main/runtime/rpc/methods/structured-agent-session-hold.ts @@ -36,7 +36,7 @@ export const STRUCTURED_AGENT_SESSION_HOLD_METHODS = [ await ensureStructuredHostInstalled(ctx) const host = requireStructuredHost(ctx) const holderKey = holderKeyFor(ctx, params.holderId) - ctx.runtime.registerSubscriptionCleanup( + const registration = ctx.runtime.registerOwnedSubscriptionCleanup( holdCleanupIdFor(params.sessionId, holderKey), () => host.release(params.sessionId, holderKey), ctx.connectionId @@ -44,7 +44,7 @@ export const STRUCTURED_AGENT_SESSION_HOLD_METHODS = [ try { await host.hold(params.sessionId, holderKey) } catch (error) { - ctx.runtime.cleanupSubscription(holdCleanupIdFor(params.sessionId, holderKey)) + registration.releaseIfCurrent() throw error } return { held: true as const } diff --git a/tests/e2e/cross-version-wire/cross-version-agent-session-wire.unit.test.ts b/tests/e2e/cross-version-wire/cross-version-agent-session-wire.unit.test.ts index 06b2a3f9248..a99e3812776 100644 --- a/tests/e2e/cross-version-wire/cross-version-agent-session-wire.unit.test.ts +++ b/tests/e2e/cross-version-wire/cross-version-agent-session-wire.unit.test.ts @@ -19,6 +19,7 @@ import type { StructuredAgentSessionAdapter } from '../../../src/main/native-cha import { StructuredAgentSessionHost } from '../../../src/main/native-chat/agent-session-wire/structured-agent-session-host' import { setStructuredAgentSessionHost } from '../../../src/main/native-chat/agent-session-wire/structured-agent-session-registry' import { AgentSessionRecordStore } from '../../../src/main/runtime/agent-session-record-store' +import { RuntimeSubscriptionRegistry } from '../../../src/main/runtime/runtime-subscription-registry' import type { AgentSessionSubscribeEvent } from '../../../src/shared/agent-session-wire' import { AGENT_SESSION_PENDING_SEND_RESULT_RUNTIME_CAPABILITY, @@ -70,7 +71,7 @@ beforeAll(async () => { }, SUITE_TIMEOUT_MS) function runtimeStub(): unknown { - const cleanups = new Map void>() + const subscriptions = new RuntimeSubscriptionRegistry() return { getRuntimeId: () => 'runtime-1', getClientSettings: () => ({ experimentalStructuredNativeChat: true }), @@ -85,19 +86,10 @@ function runtimeStub(): unknown { return resolved }, publishStructuredAgentSessionTab: () => {}, - registerSubscriptionCleanup: (id: string, cleanup: () => void) => cleanups.set(id, cleanup), - cleanupSubscription: (id: string) => { - cleanups.get(id)?.() - cleanups.delete(id) - }, - cleanupSubscriptionsByPrefix: (prefix: string) => { - for (const [id, cleanup] of cleanups) { - if (id.startsWith(prefix)) { - cleanup() - cleanups.delete(id) - } - } - } + registerSubscriptionCleanup: subscriptions.register.bind(subscriptions), + registerOwnedSubscriptionCleanup: subscriptions.registerOwned.bind(subscriptions), + cleanupSubscription: subscriptions.cleanup.bind(subscriptions), + cleanupSubscriptionsByPrefix: subscriptions.cleanupByPrefix.bind(subscriptions) } } From 1aadf9115346b28aa9acee93430e1fd47703b303 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:32:49 -0700 Subject: [PATCH 073/168] fix(runtime): preserve observed exit during explicit terminal close (#21019) * fix(pty): reconcile daemon exits after synthetic notifications * fix(runtime): preserve observed exit during explicit terminal close --------- Co-authored-by: m4air --- .../terminal-close-observed-exit/README.md | 37 ++ .../reproduce.mjs | 132 +++++++ .../terminal-close-observed-exit/results.json | 359 ++++++++++++++++++ ...runtime-stop-explicitly-closed-tab-ptys.ts | 10 + ...rminal-close-observed-exit-test-fixture.ts | 158 ++++++++ .../terminal-close-observed-exit.test.ts | 61 +++ 6 files changed, 757 insertions(+) create mode 100644 docs/audits/terminal-close-observed-exit/README.md create mode 100644 docs/audits/terminal-close-observed-exit/reproduce.mjs create mode 100644 docs/audits/terminal-close-observed-exit/results.json create mode 100644 src/main/runtime/terminal-close-observed-exit-test-fixture.ts create mode 100644 src/main/runtime/terminal-close-observed-exit.test.ts diff --git a/docs/audits/terminal-close-observed-exit/README.md b/docs/audits/terminal-close-observed-exit/README.md new file mode 100644 index 00000000000..f4d92f8ac55 --- /dev/null +++ b/docs/audits/terminal-close-observed-exit/README.md @@ -0,0 +1,37 @@ +# Preserve an observed exit during explicit terminal close + +An explicit close can receive the target daemon's physical EXIT, then fail its aggregate verification because another preserved daemon is unavailable. The close method used to invoke the fallback kill even though the runtime already held an `exited` verdict. That redundant request emitted a synthetic `-1`, replacing `operator_close` with `unknown/stop_unverified` and sending a second renderer exit notification. + +The fix captures the stamped PTY incarnation before awaiting the stop. A false stop result is accepted only when the same incarnation remains current and the runtime already has an `exited` verdict. It does not create an exit certificate from an empty inventory or transport failure. + +## Reproduce + +From the checkout, with dependencies already installed: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node docs/audits/terminal-close-observed-exit/reproduce.mjs /tmp/terminal-close-observed-exit.json +ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/runtime/terminal-close-observed-exit.test.ts +``` + +The script runs eight scenarios before and after the change, reversing only the new capture and guard for the before variant. It uses the actual runtime close method, runtime controller, daemon router, and two real daemon socket endpoints. The subprocess itself is controlled by the existing test harness. The script uses temporary configuration files, checks the expected outcomes, records source hashes, and removes its temporary directory. It does not install dependencies, launch a UI, or alter the checkout. `results.json` preserves the recorded result; use a separate output path when rerunning. + +| Scenario | Before | After | +| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | +| Target physical EXIT received; unrelated daemon unavailable | Close returns false; one redundant kill; cause overwritten; two renderer exit notifications | Close returns true; no redundant kill; `operator_close` preserved; one renderer exit notification | +| Healthy aggregate inventory, delayed physical EXIT | Close succeeds | Unchanged | +| Target socket paused; unrelated daemon unavailable | Close remains unverifiable despite target's empty inventory | Unchanged | +| Same stamped incarnation already exited | Redundant fallback kill | Existing exit accepted | +| Same raw ID registered with a newer incarnation | Old certificate rejected | Unchanged | +| Synthetic negative exit, no host exit certificate | Close remains unverifiable | Unchanged | +| Unstamped legacy session | Certificate not reused | Unchanged | +| Stop throws after exit | Catch records unverifiable | Unchanged | + +In all socket scenarios, the physical provider event and runtime exit listener settle once. The fixed observed-exit case has no headless model or title tracker retained. This proof measures lifecycle behavior, not retained heap bytes. + +## Dependency and incident limits + +This change is stacked on [#21000](https://github.com/stablyai/orca/pull/21000), branch `np-oom-scan-daemon-late-exit`, and reuses its actual daemon socket fixture and late physical-exit reconciliation. #21000 fixes final DATA arriving after a synthetic exit. This change prevents a redundant synthetic exit after a physical exit has already been accepted. The before variant is the current checkout with this narrow guard reversed, not a pristine historical build. + +The unconditional fallback and exit-cause assignment are present in the reported `v1.4.197` source (`orca-runtime-stop-explicitly-closed-tab-ptys.ts`, `orca-runtime-on-pty-exit.ts`). They explain a concrete way to get a failed close and `stop_unverified` despite a confirmed local exit. [#19018](https://github.com/stablyai/orca/issues/19018) does not establish that an unrelated preserved daemon was unavailable; this is a conditional explanation, not proof of the reporter's exact ordering. + +Generic inventory remains fail-closed. Exact-owner verification across daemon generations is separate work. This change does not solve a thrown stop, SSH loss of contact, unstamped identities, or all same-ID shutdown races. A missing diagnostics row remains insufficient evidence of process death. diff --git a/docs/audits/terminal-close-observed-exit/reproduce.mjs b/docs/audits/terminal-close-observed-exit/reproduce.mjs new file mode 100644 index 00000000000..ce8873d098d --- /dev/null +++ b/docs/audits/terminal-close-observed-exit/reproduce.mjs @@ -0,0 +1,132 @@ +import assert from 'node:assert/strict' +import { createHash } from 'node:crypto' +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import { startVitest } from 'vitest/node' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1.') +} +const root = fileURLToPath(new URL('../../../', import.meta.url)) +const sourcePath = 'src/main/runtime/orca-runtime-stop-explicitly-closed-tab-ptys.ts' +const fixturePath = 'src/main/runtime/terminal-close-observed-exit-test-fixture.ts' +const source = await readFile(join(root, sourcePath), 'utf8') +const capture = ' const expectedIncarnationId = this.ptysById.get(ptyId)?.incarnationId\n' +const guard = ` // Preserve an observed exit when a broader inventory check could not finish. + if ( + !stopped && + expectedIncarnationId && + this.ptysById.get(ptyId)?.incarnationId === expectedIncarnationId && + this.getPtyLivenessVerdict(ptyId)?.status === 'exited' + ) { + stopped = true + } +` +assert(source.includes(capture) && source.includes(guard), 'Review the baseline transform.') +const baseline = source.replace(capture, '').replace(guard, '') +const scratch = await mkdtemp(join(tmpdir(), 'orca-observed-exit-proof-')) +const phases = [] +try { + for (const phase of ['before', 'after']) { + const testPath = join(scratch, `${phase}.test.ts`) + const outputPath = join(scratch, `${phase}.json`) + const configPath = join(scratch, `${phase}.config.mjs`) + await writeFile( + testPath, + ` +import { afterAll, it } from ${JSON.stringify(join(root, 'node_modules/vitest/dist/index.js'))} +import { writeFileSync } from 'node:fs' +import { runObservedExitSocketScenario, runObservedExitControl } from ${JSON.stringify(join(root, fixturePath))} +const sockets = [] +const controls = [] +for (const scenario of ['healthy', 'unrelated-endpoint-gone', 'physical-exit-observed']) { + it(scenario, async () => sockets.push(await runObservedExitSocketScenario(scenario))) +} +for (const control of ['same-incarnation', 'replacement', 'unverified', 'legacy-unstamped', 'throw-after-exit']) { + it(control, async () => controls.push(await runObservedExitControl(control))) +} +afterAll(() => writeFileSync(${JSON.stringify(outputPath)}, JSON.stringify({ sockets, controls }))) +` + ) + await writeFile( + configPath, + ` +import base from ${JSON.stringify(pathToFileURL(join(root, 'config/vitest.config.ts')).href)} +export default { + ...base, + plugins: [{ name: 'observed-exit-baseline', enforce: 'pre', transform(code, id) { + if (id.replaceAll('\\\\', '/').endsWith(${JSON.stringify(`/${sourcePath}`)})) return ${JSON.stringify(phase === 'before' ? baseline : source)} + } }], + test: { ...base.test, include: [${JSON.stringify(testPath)}], maxWorkers: 1, fileParallelism: false } +} +` + ) + const runner = await startVitest('test', [], { + root, + config: configPath, + watch: false, + reporters: ['dot'] + }) + assert(runner, 'Vitest did not start') + await runner.close() + const result = JSON.parse(await readFile(outputPath, 'utf8')) + assert.equal(result.sockets.length, 3) + assert.equal(result.controls.length, 5) + for (const row of result.sockets) { + const observed = row.scenario === 'physical-exit-observed' + const healthy = row.scenario === 'healthy' + assert.equal(row.close.ptyKilled, healthy || (observed && phase === 'after')) + assert.equal(row.fallbackKills, healthy || (observed && phase === 'after') ? 0 : 1) + assert.equal(row.targetInventoryCount, 0) + assert.equal(row.targetProbe, false) + assert.equal(row.routerProbe, healthy ? false : null) + assert.equal(row.settled.connected, false) + assert.equal(row.settled.headlessModelRetained, false) + assert.equal(row.settled.providerExitCount, 1) + assert.equal(row.settled.exitListenerCalls, 1) + if (observed) { + assert.deepEqual( + row.settled.exitCause, + phase === 'after' + ? { kind: 'operator_close' } + : { kind: 'unknown', reason: 'stop_unverified' } + ) + assert.equal(row.settled.rendererExitCount, phase === 'after' ? 1 : 2) + } + if (!healthy && !observed) { + assert.equal(row.close.ptyStopVerdict, 'unverifiable') + assert.equal(row.beforeStreamResume.providerExitCount, 0) + } + delete row.beforeStreamResume.incarnationId + delete row.settled.incarnationId + } + for (const row of result.controls) { + const accepts = phase === 'after' && row.scenario === 'same-incarnation' + assert.equal(row.stopped, accepts) + assert.equal(row.fallbackKills, accepts ? 0 : 1) + } + phases.push({ phase, ...result }) + } + const output = `${JSON.stringify( + { + sourceHashes: { + before: createHash('sha256').update(baseline).digest('hex'), + after: createHash('sha256').update(source).digest('hex'), + fixture: createHash('sha256') + .update(await readFile(join(root, fixturePath))) + .digest('hex') + }, + phases + }, + null, + 2 + )}\n` + if (process.argv[2]) { + await writeFile(resolve(process.argv[2]), output) + } + process.stdout.write(output) +} finally { + await rm(scratch, { recursive: true, force: true }) +} diff --git a/docs/audits/terminal-close-observed-exit/results.json b/docs/audits/terminal-close-observed-exit/results.json new file mode 100644 index 00000000000..b7f79553c9a --- /dev/null +++ b/docs/audits/terminal-close-observed-exit/results.json @@ -0,0 +1,359 @@ +{ + "sourceHashes": { + "before": "963a18c7811f9bb47c5308f795edfc7ed6f7b91ccfa5c415357c701679f4204b", + "after": "36ea86fcd71c37384af31ae9b8cf8f348e4435854f5faf4829f5fafb618c2c44", + "fixture": "e9df0f19562ef2f162a6262c5052dd0e52d7770b88dc852b599e858d99452b1d" + }, + "phases": [ + { + "phase": "before", + "sockets": [ + { + "scenario": "healthy", + "close": { + "ptyKilled": true, + "ptyStopVerdict": null + }, + "fallbackKills": 0, + "targetInventoryCount": 0, + "targetProbe": false, + "routerProbe": false, + "beforeStreamResume": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 1, + "providerExitCount": 0, + "exitListenerCalls": 1 + }, + "settled": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + }, + { + "scenario": "unrelated-endpoint-gone", + "close": { + "ptyKilled": false, + "ptyStopVerdict": "unverifiable" + }, + "fallbackKills": 1, + "targetInventoryCount": 0, + "targetProbe": false, + "routerProbe": null, + "beforeStreamResume": { + "connected": false, + "exitCause": { + "kind": "unknown", + "reason": "stop_unverified" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "unverifiable", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 1, + "providerExitCount": 0, + "exitListenerCalls": 1 + }, + "settled": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + }, + { + "scenario": "physical-exit-observed", + "close": { + "ptyKilled": false, + "ptyStopVerdict": null + }, + "fallbackKills": 1, + "targetInventoryCount": 0, + "targetProbe": false, + "routerProbe": null, + "beforeStreamResume": { + "connected": false, + "exitCause": { + "kind": "unknown", + "reason": "stop_unverified" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 2, + "providerExitCount": 1, + "exitListenerCalls": 1 + }, + "settled": { + "connected": false, + "exitCause": { + "kind": "unknown", + "reason": "stop_unverified" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 2, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + } + ], + "controls": [ + { + "scenario": "same-incarnation", + "stopped": false, + "fallbackKills": 1, + "verdict": { + "status": "exited" + } + }, + { + "scenario": "replacement", + "stopped": false, + "fallbackKills": 1, + "verdict": { + "status": "unverifiable", + "reason": "a follow-up stop was issued but its outcome could not be verified" + } + }, + { + "scenario": "unverified", + "stopped": false, + "fallbackKills": 1, + "verdict": { + "status": "unverifiable", + "reason": "a follow-up stop was issued but its outcome could not be verified" + } + }, + { + "scenario": "legacy-unstamped", + "stopped": false, + "fallbackKills": 1, + "verdict": { + "status": "exited" + } + }, + { + "scenario": "throw-after-exit", + "stopped": false, + "fallbackKills": 1, + "verdict": { + "status": "unverifiable", + "reason": "unverified transport failure" + } + } + ] + }, + { + "phase": "after", + "sockets": [ + { + "scenario": "healthy", + "close": { + "ptyKilled": true, + "ptyStopVerdict": null + }, + "fallbackKills": 0, + "targetInventoryCount": 0, + "targetProbe": false, + "routerProbe": false, + "beforeStreamResume": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 1, + "providerExitCount": 0, + "exitListenerCalls": 1 + }, + "settled": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + }, + { + "scenario": "unrelated-endpoint-gone", + "close": { + "ptyKilled": false, + "ptyStopVerdict": "unverifiable" + }, + "fallbackKills": 1, + "targetInventoryCount": 0, + "targetProbe": false, + "routerProbe": null, + "beforeStreamResume": { + "connected": false, + "exitCause": { + "kind": "unknown", + "reason": "stop_unverified" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "unverifiable", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 1, + "providerExitCount": 0, + "exitListenerCalls": 1 + }, + "settled": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + }, + { + "scenario": "physical-exit-observed", + "close": { + "ptyKilled": true, + "ptyStopVerdict": null + }, + "fallbackKills": 0, + "targetInventoryCount": 0, + "targetProbe": false, + "routerProbe": null, + "beforeStreamResume": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + }, + "settled": { + "connected": false, + "exitCause": { + "kind": "operator_close" + }, + "headlessModelRetained": false, + "titleTrackerRetained": false, + "liveness": "exited", + "providerHasPty": false, + "hostInventoryCount": 0, + "deliveredData": [], + "rendererExitCount": 1, + "providerExitCount": 1, + "exitListenerCalls": 1 + } + } + ], + "controls": [ + { + "scenario": "same-incarnation", + "stopped": true, + "fallbackKills": 0, + "verdict": { + "status": "exited" + } + }, + { + "scenario": "replacement", + "stopped": false, + "fallbackKills": 1, + "verdict": { + "status": "unverifiable", + "reason": "a follow-up stop was issued but its outcome could not be verified" + } + }, + { + "scenario": "unverified", + "stopped": false, + "fallbackKills": 1, + "verdict": { + "status": "unverifiable", + "reason": "a follow-up stop was issued but its outcome could not be verified" + } + }, + { + "scenario": "legacy-unstamped", + "stopped": false, + "fallbackKills": 1, + "verdict": { + "status": "exited" + } + }, + { + "scenario": "throw-after-exit", + "stopped": false, + "fallbackKills": 1, + "verdict": { + "status": "unverifiable", + "reason": "unverified transport failure" + } + } + ] + } + ] +} diff --git a/src/main/runtime/orca-runtime-stop-explicitly-closed-tab-ptys.ts b/src/main/runtime/orca-runtime-stop-explicitly-closed-tab-ptys.ts index 1433ecfdb0e..196845d09ef 100644 --- a/src/main/runtime/orca-runtime-stop-explicitly-closed-tab-ptys.ts +++ b/src/main/runtime/orca-runtime-stop-explicitly-closed-tab-ptys.ts @@ -15,6 +15,7 @@ export class OrcaRuntimeWithStopExplicitlyClosedTabPtys extends OrcaRuntimeWithF const deadlineMs = Date.now() + EXPLICIT_TERMINAL_CLOSE_STOP_TIMEOUT_MS for (const ptyId of ptyIds) { this.markPtyStopRequested(ptyId) + const expectedIncarnationId = this.ptysById.get(ptyId)?.incarnationId let stopped = false if (this.ptyController?.stopAndWait) { try { @@ -25,6 +26,15 @@ export class OrcaRuntimeWithStopExplicitlyClosedTabPtys extends OrcaRuntimeWithF error instanceof Error ? error.message : String(error) ) } + // Preserve an observed exit when a broader inventory check could not finish. + if ( + !stopped && + expectedIncarnationId && + this.ptysById.get(ptyId)?.incarnationId === expectedIncarnationId && + this.getPtyLivenessVerdict(ptyId)?.status === 'exited' + ) { + stopped = true + } if (!stopped) { const verdict = this.getPtyLivenessVerdict(ptyId) const providerAlreadyRetiredPty = diff --git a/src/main/runtime/terminal-close-observed-exit-test-fixture.ts b/src/main/runtime/terminal-close-observed-exit-test-fixture.ts new file mode 100644 index 00000000000..468508f896d --- /dev/null +++ b/src/main/runtime/terminal-close-observed-exit-test-fixture.ts @@ -0,0 +1,158 @@ +import { rmSync } from 'node:fs' +import { DaemonPtyRouter } from '../daemon/daemon-pty-router' +import { + createMockSubprocess, + startDaemonAdapterHarness +} from '../daemon/daemon-pty-adapter-test-harness' +import { startLateExitHarness } from '../ipc/pty/daemon-late-exit-test-fixture' +import { bindProviderListeners } from '../ipc/pty/provider/bind-listeners' +import { finishPtyShutdown } from '../ipc/pty/provider/liveness' +import { setLocalPtyProvider } from '../ipc/pty/provider/registry' +import { shutdownProviderAndDetectExit } from '../ipc/pty/provider/shutdown-detect' +import type { PtyRuntimeControllerDeps } from '../ipc/pty/runtime/controller-deps' +import { + killPtyFromRuntimeController, + stopAndWaitPtyFromRuntimeController +} from '../ipc/pty/runtime/kill' +import { OrcaRuntimeService } from './orca-runtime' + +export type ObservedExitSocketScenario = + | 'healthy' + | 'unrelated-endpoint-gone' + | 'physical-exit-observed' + +export async function runObservedExitSocketScenario(scenario: ObservedExitSocketScenario) { + const harness = await startLateExitHarness() + const legacy = await startDaemonAdapterHarness(() => createMockSubprocess()) + const router = new DaemonPtyRouter({ current: harness.adapter, legacy: [legacy.adapter] }) + let fallbackKills = 0 + try { + await router.discoverLegacySessions() + setLocalPtyProvider(router) + bindProviderListeners(harness.session) + const ports = { + runtime: harness.runtime, + getLocalPtyProviderStartupPromise: () => undefined, + shutdownProviderAndDetectExit, + rememberSyntheticKillExit: harness.session.rememberSyntheticKillExit, + sendPtyExitToRenderer: harness.session.sendPtyExitToRenderer, + finishPtyShutdown, + retiredRejectedPtyIds: new Map(), + reversibleStopOwnersByPtyId: new Map() + } + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: stop/kill read only these controller ports and optional store; spawn ports are unused. + const deps = ports as unknown as PtyRuntimeControllerDeps + harness.runtime.setPtyController({ + write: () => true, + getForegroundProcess: async () => null, + kill: (id) => { + fallbackKills++ + return killPtyFromRuntimeController(deps, id) + }, + stopAndWait: (id) => + stopAndWaitPtyFromRuntimeController(deps, id, { deadlineMs: Date.now() + 1_500 }) + }) + const list = await harness.runtime.listTerminals() + const terminal = list.terminals.find((entry) => entry.ptyId === harness.id) + if (!terminal) { + throw new Error('Fixture terminal missing') + } + if (scenario !== 'healthy') { + await legacy.server.shutdown() + } + if (scenario !== 'physical-exit-observed') { + harness.pauseStream() + } + const close = await harness.runtime.closeTerminal(terminal.handle) + const targetInventory = await harness.adapter.listProcesses() + const targetProbe = await harness.adapter.probePtyLiveness(harness.id) + const routerProbe = await router.probePtyLiveness(harness.id) + const beforeStreamResume = await harness.capture() + harness.resumeStream() + await harness.waitForExit() + const settled = await harness.capture() + return { + scenario, + close: { + ptyKilled: close.ptyKilled, + ptyStopVerdict: close.ptyStopVerdict ?? null + }, + fallbackKills, + targetInventoryCount: targetInventory.length, + targetProbe, + routerProbe, + beforeStreamResume, + settled + } + } finally { + router.disposeRouterOnly() + await harness.dispose() + legacy.adapter.dispose() + await legacy.server.shutdown() + rmSync(legacy.dir, { recursive: true, force: true }) + } +} + +const CONTROL_PTY_ID = 'repo::/tmp/observed-exit-control@@pty' +const WORKTREE_ID = 'repo::/tmp/observed-exit-control' +const FIRST_INCARNATION = '10000000-0000-4000-8000-000000000001' +const NEXT_INCARNATION = '10000000-0000-4000-8000-000000000002' +const BINDING = { + tabId: 'control-tab', + leafId: '10000000-0000-4000-8000-000000000004' +} + +class ObservedExitRuntime extends OrcaRuntimeService { + closeControl(): Promise { + return this.stopExplicitlyClosedTabPtys([CONTROL_PTY_ID], CONTROL_PTY_ID) + } +} + +export type ObservedExitControl = + | 'same-incarnation' + | 'replacement' + | 'unverified' + | 'legacy-unstamped' + | 'throw-after-exit' + +export async function runObservedExitControl(control: ObservedExitControl) { + const runtime = new ObservedExitRuntime() + const original = control === 'legacy-unstamped' ? undefined : FIRST_INCARNATION + let fallbackKills = 0 + runtime.registerPty(CONTROL_PTY_ID, WORKTREE_ID, null, { + ...BINDING, + ...(original ? { incarnationId: original } : {}) + }) + runtime.setPtyController({ + write: () => true, + kill: () => { + fallbackKills++ + return true + }, + getForegroundProcess: async () => null, + stopAndWait: async () => { + runtime.onPtyExit(CONTROL_PTY_ID, control === 'unverified' ? -1 : 0, original) + if (control === 'replacement') { + runtime.registerPty(CONTROL_PTY_ID, WORKTREE_ID, null, { + ...BINDING, + incarnationId: NEXT_INCARNATION + }) + } + if (control === 'throw-after-exit') { + throw new Error('unverified transport failure') + } + return false + } + }) + try { + const stopped = await runtime.closeControl() + return { + scenario: control, + stopped, + fallbackKills, + verdict: runtime.getPtyLivenessVerdict(CONTROL_PTY_ID) + } + } finally { + runtime.onPtyExit(CONTROL_PTY_ID, 0, control === 'replacement' ? NEXT_INCARNATION : original) + } +} diff --git a/src/main/runtime/terminal-close-observed-exit.test.ts b/src/main/runtime/terminal-close-observed-exit.test.ts new file mode 100644 index 00000000000..b41f61cfcc8 --- /dev/null +++ b/src/main/runtime/terminal-close-observed-exit.test.ts @@ -0,0 +1,61 @@ +import { describe, expect, it } from 'vitest' +import { + runObservedExitControl, + runObservedExitSocketScenario +} from './terminal-close-observed-exit-test-fixture' + +describe('closing a terminal after observing its physical exit', () => { + it('preserves the physical cause when an unrelated daemon prevents aggregate verification', async () => { + const result = await runObservedExitSocketScenario('physical-exit-observed') + expect(result.close).toEqual({ ptyKilled: true, ptyStopVerdict: null }) + expect(result.fallbackKills).toBe(0) + expect(result.targetInventoryCount).toBe(0) + expect(result.targetProbe).toBe(false) + expect(result.routerProbe).toBeNull() + expect(result.settled).toMatchObject({ + connected: false, + exitCause: { kind: 'operator_close' }, + headlessModelRetained: false, + titleTrackerRetained: false, + liveness: 'exited', + rendererExitCount: 1, + providerExitCount: 1, + exitListenerCalls: 1 + }) + }) + + it('keeps the healthy aggregate verification and delayed physical exit behavior', async () => { + const result = await runObservedExitSocketScenario('healthy') + expect(result.close.ptyKilled).toBe(true) + expect(result.fallbackKills).toBe(0) + expect(result.routerProbe).toBe(false) + expect(result.settled.exitCause).toEqual({ kind: 'operator_close' }) + expect(result.settled.rendererExitCount).toBe(1) + expect(result.settled.exitListenerCalls).toBe(1) + }) + + it('does not treat target absence as an earned exit before the stream delivers it', async () => { + const result = await runObservedExitSocketScenario('unrelated-endpoint-gone') + expect(result.close).toEqual({ ptyKilled: false, ptyStopVerdict: 'unverifiable' }) + expect(result.fallbackKills).toBe(1) + expect(result.targetProbe).toBe(false) + expect(result.routerProbe).toBeNull() + expect(result.beforeStreamResume.providerExitCount).toBe(0) + }) + + it('uses a stamped exit for the incarnation that was actually being closed', async () => { + const result = await runObservedExitControl('same-incarnation') + expect(result.stopped).toBe(true) + expect(result.fallbackKills).toBe(0) + expect(result.verdict?.status).toBe('exited') + }) + + it.each(['replacement', 'unverified', 'legacy-unstamped', 'throw-after-exit'] as const)( + 'does not reuse an exit for %s', + async (control) => { + const result = await runObservedExitControl(control) + expect(result.stopped).toBe(false) + expect(result.fallbackKills).toBe(1) + } + ) +}) From 57e28ccf7c523b57881be0b857fcc8e171a17b05 Mon Sep 17 00:00:00 2001 From: Lesley Murfin Date: Thu, 17 Sep 2026 21:33:36 -0600 Subject: [PATCH 074/168] fix(runtime): keep absent session tab close intents durable (#21189) (#21277) * fix(runtime): treat selector_not_found as definitive tab absence (#21189) When closing a tab whose worktree selector is absent, propagate the error through host RPC and classify it as unknown-tab on the renderer to engage durable tombstones and prevent resurrection loops. Pin host RPC error propagation with dedicated regression tests. Co-authored-by: Neil Parker * test(runtime): remove invalid absent-tab Docker spec The spec dynamically imported renderer source from the browser and did not exercise a real close RPC. Keep the executable renderer and host regression coverage instead.\n\nCo-authored-by: Lesley Murfin * fix(runtime): narrow durable tab absence to tab and terminal absence (#21189) Narrow durable close tombstones in web-runtime-session-tab-lifecycle to tab_not_found and terminal_tab_not_found. In production, session tab close requests pass explicit `id:` worktree selectors and take the fast path in closeMobileSessionTab, bypassing resolveWorktreeSelector. Transient selector_not_found errors retain normal TTL eviction. --------- Co-authored-by: Neil Parker --- .../web-runtime-session-tab-activate-close.test.ts | 7 +++++++ .../runtime/web-runtime-session-tab-lifecycle.ts | 13 +++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/renderer/src/runtime/web-runtime-session-tab-activate-close.test.ts b/src/renderer/src/runtime/web-runtime-session-tab-activate-close.test.ts index b535bd31836..495640cde79 100644 --- a/src/renderer/src/runtime/web-runtime-session-tab-activate-close.test.ts +++ b/src/renderer/src/runtime/web-runtime-session-tab-activate-close.test.ts @@ -288,8 +288,12 @@ describe('web runtime session tab actions', () => { // Why this distinction is load-bearing: a close that reports 'unknown-tab' lets the client // finish a teardown the host cannot, and reporting it for an ordinary failure would tear down // tabs a reachable host still holds. + // Note: 'selector_not_found' is a transient scan cache miss during worktree discovery, not + // definitive absence proof, so it classifies as 'failed' and must not drop TTL eviction. it.each([ ['tab_not_found', 'unknown-tab'], + ['selector_not_found', 'failed'], + ['terminal_tab_not_found', 'unknown-tab'], ['runtime_rpc_timeout', 'failed'] ])('classifies a %s close refusal as %s', async (code, outcome) => { const runtimeCall = vi @@ -310,8 +314,11 @@ describe('web runtime session tab actions', () => { // #9194: a host can answer tab_not_found and still keep republishing the surface. The close // intent is what hides the mirror, so letting it age out handed the user back a phantom pane // whose handle is already gone -- and closing it again just restarted the same TTL loop. + // 'selector_not_found' is transient, so it does not become durable and its suppression expires. it.each([ ['tab_not_found', true], + ['selector_not_found', false], + ['terminal_tab_not_found', true], ['runtime_rpc_timeout', false] ])('keeps a %s close suppressed past the close-intent TTL: %s', async (code, stillPending) => { const runtimeCall = vi diff --git a/src/renderer/src/runtime/web-runtime-session-tab-lifecycle.ts b/src/renderer/src/runtime/web-runtime-session-tab-lifecycle.ts index 56800f3cefb..0fbb18fb534 100644 --- a/src/renderer/src/runtime/web-runtime-session-tab-lifecycle.ts +++ b/src/renderer/src/runtime/web-runtime-session-tab-lifecycle.ts @@ -162,10 +162,15 @@ async function callWebRuntimeSessionTabMethod( if (activationHostTabId) { clearWebSessionFocusIntentIfMatches(intentOwner, args.worktreeId, activationHostTabId) } - // Why the split: only 'tab_not_found' is absence proof (see the outcome doc above). Restoring the - // mirror on it hands the user back a pane the host cannot close and whose handle is already gone - // (#9194), so keep the suppression and drop its TTL instead. Every other failure is a "not now". - const hostHasNoSuchTab = hasRuntimeRpcErrorCode(error, 'tab_not_found') + // Why the split: 'tab_not_found' and 'terminal_tab_not_found' prove definitive surface absence. + // Restoring the mirror on it hands the user back a pane the host cannot close and whose handle is already gone (#9194, #21189), + // so keep the suppression and drop its TTL instead. + // 'selector_not_found' is a transient worktree resolver state (e.g. during scans or cache warm-up, + // per remote-browser-stream-errors.ts) and must not become a durable close tombstone. + // Every other failure is a "not now". + const hostHasNoSuchTab = + hasRuntimeRpcErrorCode(error, 'tab_not_found') || + hasRuntimeRpcErrorCode(error, 'terminal_tab_not_found') for (const hostTabId of closeIntentTabIds) { if (hostHasNoSuchTab) { makeWebSessionCloseIntentDurable(intentOwner, args.worktreeId, hostTabId) From d04b05b5c8f0b8074a7e316c8cd6ccdbbddf7e2c Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:34:19 -0700 Subject: [PATCH 075/168] Detach retained CI and terminal tails from oversized strings (#20960) * fix(memory): detach retained CI and terminal tails from oversized strings * fix(terminal): detach retained error and reattach string slices * fix(terminal): release oversized recent-output backing strings * fix(terminal): release backing strings held by PTY detectors * fix(memory): own bounded Claude background task labels * fix: detach retained terminal mode scan tails * fix: own retained plugin worker output strings * fix: own incomplete OSC 133 carry strings --------- Co-authored-by: m4air Co-authored-by: m4air --- docs/audits/claude-task-retention/README.md | 64 + .../claude-task-retention/reproduce.cjs | 197 +++ .../audits/claude-task-retention/results.json | 370 ++++ docs/audits/osc133-carry-retention/README.md | 106 ++ .../osc133-carry-retention/before.config.mjs | 23 + .../electron-results.json | 1535 +++++++++++++++++ docs/audits/osc133-carry-retention/fix.patch | 7 + .../osc133-carry-retention/node-results.json | 1535 +++++++++++++++++ .../osc133-carry-retention/reproduce.cjs | 142 ++ .../osc133-carry-retention/scenario.cjs | 99 ++ .../source-versions.json | 646 +++++++ .../audits/osc133-carry-retention/sources.cjs | 87 + .../osc133-carry-retention/validation.json | 83 + .../plugin-worker-output-retention/README.md | 67 + .../before.config.mjs | 23 + .../electron-results.json | 493 ++++++ .../plugin-worker-output-retention/fix.patch | 18 + .../node-results.json | 492 ++++++ .../reproduce.cjs | 249 +++ .../source-versions.json | 89 + .../sources.cjs | 94 + docs/audits/pty-detector-retention/README.md | 59 + .../pty-detector-retention/reproduce.mjs | 144 ++ .../pty-detector-retention/results.json | 94 + docs/audits/retained-text-slices/README.md | 75 + .../audits/retained-text-slices/reproduce.mjs | 161 ++ docs/audits/retained-text-slices/results.json | 140 ++ .../terminal-mode-tail-retention/README.md | 122 ++ .../electron-results.json | 467 +++++ .../load-source.cjs | 68 + .../node-results.json | 467 +++++ .../reproduce.cjs | 205 +++ .../source-versions.json | 44 + .../claude/claude-background-task-frames.ts | 3 +- .../claude-background-task-retention.test.ts | 96 ++ src/main/daemon/terminal-mouse-mode-mirror.ts | 5 +- .../terminal-mouse-tail-retention.test.ts | 41 + .../plugins/plugin-worker-output-buffer.ts | 11 +- .../plugin-worker-output-retention.test.ts | 82 + src/main/ports/advertised-url-parsing.ts | 3 +- .../ports/advertised-url-retention.test.ts | 44 + src/main/ports/advertised-url-watcher.ts | 7 +- src/main/runtime/recent-pty-output-buffer.ts | 4 +- .../recent-pty-output-retention.test.ts | 35 + .../deferred-reattach-live-data-queue.ts | 5 +- .../terminal-pane/pty-eager-buffer-clamp.ts | 6 +- .../terminal-capped-buffer-retention.test.ts | 92 + .../terminal-error-accumulation.ts | 6 +- src/shared/check-job-log-retention.test.ts | 45 + src/shared/check-job-log-tail-slice.ts | 8 +- .../command-code-output-retention.test.ts | 26 + src/shared/command-code-output-status.ts | 3 +- .../terminal-kitty-keyboard-mode-tracker.ts | 3 +- ...inal-kitty-keyboard-tail-retention.test.ts | 41 + .../terminal-osc133-carry-retention.test.ts | 109 ++ .../terminal-osc133-command-finished.ts | 3 + .../workspace-session-terminal-buffers.ts | 5 +- 57 files changed, 9130 insertions(+), 18 deletions(-) create mode 100644 docs/audits/claude-task-retention/README.md create mode 100644 docs/audits/claude-task-retention/reproduce.cjs create mode 100644 docs/audits/claude-task-retention/results.json create mode 100644 docs/audits/osc133-carry-retention/README.md create mode 100644 docs/audits/osc133-carry-retention/before.config.mjs create mode 100644 docs/audits/osc133-carry-retention/electron-results.json create mode 100644 docs/audits/osc133-carry-retention/fix.patch create mode 100644 docs/audits/osc133-carry-retention/node-results.json create mode 100644 docs/audits/osc133-carry-retention/reproduce.cjs create mode 100644 docs/audits/osc133-carry-retention/scenario.cjs create mode 100644 docs/audits/osc133-carry-retention/source-versions.json create mode 100644 docs/audits/osc133-carry-retention/sources.cjs create mode 100644 docs/audits/osc133-carry-retention/validation.json create mode 100644 docs/audits/plugin-worker-output-retention/README.md create mode 100644 docs/audits/plugin-worker-output-retention/before.config.mjs create mode 100644 docs/audits/plugin-worker-output-retention/electron-results.json create mode 100644 docs/audits/plugin-worker-output-retention/fix.patch create mode 100644 docs/audits/plugin-worker-output-retention/node-results.json create mode 100644 docs/audits/plugin-worker-output-retention/reproduce.cjs create mode 100644 docs/audits/plugin-worker-output-retention/source-versions.json create mode 100644 docs/audits/plugin-worker-output-retention/sources.cjs create mode 100644 docs/audits/pty-detector-retention/README.md create mode 100644 docs/audits/pty-detector-retention/reproduce.mjs create mode 100644 docs/audits/pty-detector-retention/results.json create mode 100644 docs/audits/retained-text-slices/README.md create mode 100644 docs/audits/retained-text-slices/reproduce.mjs create mode 100644 docs/audits/retained-text-slices/results.json create mode 100644 docs/audits/terminal-mode-tail-retention/README.md create mode 100644 docs/audits/terminal-mode-tail-retention/electron-results.json create mode 100644 docs/audits/terminal-mode-tail-retention/load-source.cjs create mode 100644 docs/audits/terminal-mode-tail-retention/node-results.json create mode 100644 docs/audits/terminal-mode-tail-retention/reproduce.cjs create mode 100644 docs/audits/terminal-mode-tail-retention/source-versions.json create mode 100644 src/main/claude/claude-background-task-retention.test.ts create mode 100644 src/main/daemon/terminal-mouse-tail-retention.test.ts create mode 100644 src/main/plugins/plugin-worker-output-retention.test.ts create mode 100644 src/main/ports/advertised-url-retention.test.ts create mode 100644 src/main/runtime/recent-pty-output-retention.test.ts create mode 100644 src/renderer/src/components/terminal-pane/terminal-capped-buffer-retention.test.ts create mode 100644 src/shared/check-job-log-retention.test.ts create mode 100644 src/shared/command-code-output-retention.test.ts create mode 100644 src/shared/terminal-kitty-keyboard-tail-retention.test.ts create mode 100644 src/shared/terminal-osc133-carry-retention.test.ts diff --git a/docs/audits/claude-task-retention/README.md b/docs/audits/claude-task-retention/README.md new file mode 100644 index 00000000000..da40c172667 --- /dev/null +++ b/docs/audits/claude-task-retention/README.md @@ -0,0 +1,64 @@ +# Retained Claude background-task text + +The actual Claude task tracker retained oversized input strings through its +512-character description/name slices. Its live tasks, settled tasks, and +recently removed tasks can each retain those slices. The fix uses the existing +`ownRetainedString` at the shared text boundary; normalization, UTF-16 clipping, +task identity, publication, and lifecycle behavior stay the same. + +This extends [ML-018 / #20960](https://github.com/stablyai/orca/pull/20960). + +## Reproduce + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc docs/audits/claude-task-retention/reproduce.cjs +``` + +The script bundles the actual tracker and its retention classes. Its baseline +removes only the new copy call in memory. It exercises flat strings, concatenated +strings, and JSON-parsed SDK-style frames; each input has a distinct task owner. +It measures after GC, then clears the tracker and yields before measuring cleanup. +[Results and bundle hashes](./results.json) preserve the complete run. + +| JSON-parsed case | Input per task | Tasks | Visible text | Heap before | Heap after | +| ------------------------- | ---------------: | ----: | ----------------: | ----------: | ---------: | +| Live | 64 Ki characters | 32 | 16,384 characters | 2,125,672 | 43,536 | +| Settled | 64 Ki characters | 32 | 16,384 characters | 2,127,072 | 44,296 | +| Removed, awaiting outcome | 64 Ki characters | 32 | 0 characters | 2,108,136 | 25,360 | +| Live | 4 Mi characters | 8 | 4,096 characters | 33,562,624 | 11,048 | +| Settled | 4 Mi characters | 8 | 4,096 characters | 33,563,960 | 11,656 | +| Removed, awaiting outcome | 4 Mi characters | 8 | 0 characters | 33,558,584 | 7,008 | + +Captured with Node v26.6.0 on macOS. Cleanup returned near the initial heap for +every case. Six regression tests retain the actual tracker through these three +lifetimes for both descriptions and names. Text behavior tests preserve whitespace +normalization, fallback names, and a clipped surrogate pair. + +## Reachability and limits + +`claude-stream-json-connection.ts` forwards SDK messages to the structured adapter, +whose `emit` calls `backgroundTasks.observe`. Installed SDK 0.3.251 uses Node +`readline` to assemble stdout records, parses each record with `JSON.parse`, then +yields it. The inspected path imposes no record or description length limit; +native read-chunk size does not cap an assembled JSON field. Descriptions are +declared as plain strings in `SDKTaskStartedMessage`. + +The description slice and this SDK version also exist in `v1.4.198`; that tag +keeps the reader inline in `claude-background-task-tracker.ts`. The separate +settled/recently-removed retention and name-reader paths describe current code. + +The current maps are count-bounded: at most 256 live, 256 settled, and 256 recently +removed entries per tracker. Settled context clears when no visible work remains; +recently removed context awaits an outcome, eviction, or explicit clearing. +Session end/close clears the tracker. Copy work is at most 512 UTF-16 code units +per retained field, and it does not reduce temporary parsing allocation. + +These are synthetic oversized task fields, not evidence that an affected user +received such fields. The path concerns structured Claude sessions, not ordinary +terminal output or stderr. Neither #19831 nor #19768 establishes this trigger. + +The separate digest-bounded subagent ID was also checked at actual consumers. +The mobile response sanitizer can temporarily retain the original until JSON +serialization flattens its concatenated ID. Worker transcript bounding already +serializes for its byte budget and released that parent in the probe. No durable +ID-owner leak was established, so that helper is unchanged. diff --git a/docs/audits/claude-task-retention/reproduce.cjs b/docs/audits/claude-task-retention/reproduce.cjs new file mode 100644 index 00000000000..5910c10f847 --- /dev/null +++ b/docs/audits/claude-task-retention/reproduce.cjs @@ -0,0 +1,197 @@ +const fs = require('node:fs') +const { build } = require('esbuild') +const assert = require('node:assert/strict') +const path = require('node:path') +const { tmpdir } = require('node:os') +const { createHash } = require('node:crypto') +const root = path.resolve(__dirname, '../../..') +const bundles = {} + +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function') + +async function loadTracker(fixed) { + const result = await build({ + entryPoints: [path.join(root, 'src/main/claude/claude-background-task-tracker.ts')], + bundle: true, + write: false, + platform: 'node', + format: 'cjs', + target: 'node22', + plugins: fixed + ? [] + : [ + { + name: 'baseline-without-task-text-copy', + setup(builder) { + builder.onLoad({ filter: /claude-background-task-frames\.ts$/ }, (args) => { + const source = fs.readFileSync(args.path, 'utf8') + const boundary = 'ownRetainedString(trimmed.slice(0, MAX_TASK_TEXT_LENGTH))' + assert.ok( + source.includes(boundary), + 'The copy boundary changed; update the baseline transform' + ) + return { + loader: 'ts', + contents: source.replace(boundary, 'trimmed.slice(0, MAX_TASK_TEXT_LENGTH)') + } + }) + } + } + ] + }) + bundles[fixed ? 'after' : 'before'] = createHash('sha256') + .update(result.outputFiles[0].text) + .digest('hex') + const scratch = fs.mkdtempSync(path.join(tmpdir(), 'orca-claude-task-proof-')) + let moduleId + try { + const bundlePath = path.join(scratch, 'tracker.cjs') + fs.writeFileSync(bundlePath, result.outputFiles[0].text) + moduleId = require.resolve(bundlePath) + return require(moduleId).ClaudeBackgroundTaskTracker + } finally { + if (moduleId) { + delete require.cache[moduleId] + } + fs.rmSync(scratch, { recursive: true, force: true }) + } +} + +function collect() { + for (let i = 0; i < 5; i++) { + global.gc() + } + return process.memoryUsage().heapUsed +} + +const settle = () => new Promise((resolve) => setImmediate(resolve)) + +function frame(index, size, ingress, field) { + const value = String.fromCharCode(65 + (index % 26)).repeat(size) + const message = { + type: 'system', + subtype: 'task_started', + task_id: `task-${index}`, + task_type: 'local_bash', + is_backgrounded: true, + [field]: value + } + if (ingress === 'json') { + return JSON.parse(JSON.stringify(message)) + } + if (ingress === 'flat') { + value.charCodeAt(value.length - 1) + } + return message +} + +function populate(Tracker, { count, size, ingress, retention, field }) { + const owner = new Tracker() + const keeper = { + type: 'system', + subtype: 'task_started', + task_id: 'keeper', + task_type: 'local_bash', + is_backgrounded: true + } + if (retention !== 'live') { + owner.observe(keeper) + } + for (let index = 0; index < count; index++) { + owner.observe(frame(index, size, ingress, field)) + if (retention === 'settled') { + owner.observe({ + type: 'system', + subtype: 'task_notification', + task_id: `task-${index}`, + status: 'completed' + }) + } + } + if (retention === 'removed') { + owner.observe({ type: 'system', subtype: 'background_tasks_changed', tasks: [keeper] }) + } + return owner +} + +function logicalChars(owner) { + const state = owner.state + return [...(state?.tasks ?? []), ...(state?.settledTasks ?? [])].reduce( + (sum, task) => sum + (task.description?.length ?? 0) + (task.name?.length ?? 0), + 0 + ) +} + +async function main() { + const Before = await loadTracker(false) + const Fixed = await loadTracker(true) + for (const Tracker of [Before, Fixed]) { + const warm = populate(Tracker, { + count: 1, + size: 1024, + ingress: 'json', + retention: 'live', + field: 'description' + }) + warm.clear() + } + const results = [] + for (const [count, size] of [ + [32, 64 * 1024], + [8, 4 * 1024 * 1024] + ]) { + for (const ingress of ['flat', 'cons', 'json']) { + for (const retention of ['live', 'settled', 'removed']) { + for (const [phase, Tracker] of [ + ['before', Before], + ['after', Fixed] + ]) { + await settle() + const baseline = collect() + global.auditTaskOwner = populate(Tracker, { + count, + size, + ingress, + retention, + field: 'description' + }) + await settle() + const retainedHeapBytes = collect() - baseline + const visibleTextChars = logicalChars(global.auditTaskOwner) + global.auditTaskOwner.clear() + global.auditTaskOwner = null + await settle() + const afterClearHeapBytes = collect() - baseline + if (phase === 'after') { + assert.ok(retainedHeapBytes < 1024 * 1024, 'A bounded task retained its parent frame') + } else { + assert.ok( + retainedHeapBytes > count * size * 0.75, + 'Baseline no longer reproduces retention' + ) + } + assert.ok(afterClearHeapBytes < 1024 * 1024, 'Tracker cleanup retained the fixture') + results.push({ + count, + size, + ingress, + retention, + phase, + visibleTextChars, + retainedHeapBytes, + afterClearHeapBytes + }) + } + } + } + } + console.log( + JSON.stringify({ node: process.version, platform: process.platform, bundles, results }, null, 2) + ) +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/docs/audits/claude-task-retention/results.json b/docs/audits/claude-task-retention/results.json new file mode 100644 index 00000000000..8d337835310 --- /dev/null +++ b/docs/audits/claude-task-retention/results.json @@ -0,0 +1,370 @@ +{ + "node": "v26.6.0", + "platform": "darwin", + "bundles": { + "before": "95425d0894ed107d85a671238f0229e6db3e229c0fa94286d1bea1a52db7112f", + "after": "e1e5f58ddba3aeea88922be927509754b766247b81b6dcf7675b49a25c3a8d29" + }, + "results": [ + { + "count": 32, + "size": 65536, + "ingress": "flat", + "retention": "live", + "phase": "before", + "visibleTextChars": 16384, + "retainedHeapBytes": 2159976, + "afterClearHeapBytes": 32392 + }, + { + "count": 32, + "size": 65536, + "ingress": "flat", + "retention": "live", + "phase": "after", + "visibleTextChars": 16384, + "retainedHeapBytes": 97680, + "afterClearHeapBytes": 51600 + }, + { + "count": 32, + "size": 65536, + "ingress": "flat", + "retention": "settled", + "phase": "before", + "visibleTextChars": 16384, + "retainedHeapBytes": 2140416, + "afterClearHeapBytes": 15320 + }, + { + "count": 32, + "size": 65536, + "ingress": "flat", + "retention": "settled", + "phase": "after", + "visibleTextChars": 16384, + "retainedHeapBytes": 55816, + "afterClearHeapBytes": 12976 + }, + { + "count": 32, + "size": 65536, + "ingress": "flat", + "retention": "removed", + "phase": "before", + "visibleTextChars": 0, + "retainedHeapBytes": 2112656, + "afterClearHeapBytes": 5200 + }, + { + "count": 32, + "size": 65536, + "ingress": "flat", + "retention": "removed", + "phase": "after", + "visibleTextChars": 0, + "retainedHeapBytes": 36568, + "afterClearHeapBytes": 11832 + }, + { + "count": 32, + "size": 65536, + "ingress": "cons", + "retention": "live", + "phase": "before", + "visibleTextChars": 16384, + "retainedHeapBytes": 2125456, + "afterClearHeapBytes": -304 + }, + { + "count": 32, + "size": 65536, + "ingress": "cons", + "retention": "live", + "phase": "after", + "visibleTextChars": 16384, + "retainedHeapBytes": 42736, + "afterClearHeapBytes": -304 + }, + { + "count": 32, + "size": 65536, + "ingress": "cons", + "retention": "settled", + "phase": "before", + "visibleTextChars": 16384, + "retainedHeapBytes": 2127096, + "afterClearHeapBytes": 464 + }, + { + "count": 32, + "size": 65536, + "ingress": "cons", + "retention": "settled", + "phase": "after", + "visibleTextChars": 16384, + "retainedHeapBytes": 43896, + "afterClearHeapBytes": 368 + }, + { + "count": 32, + "size": 65536, + "ingress": "cons", + "retention": "removed", + "phase": "before", + "visibleTextChars": 0, + "retainedHeapBytes": 2123696, + "afterClearHeapBytes": 16216 + }, + { + "count": 32, + "size": 65536, + "ingress": "cons", + "retention": "removed", + "phase": "after", + "visibleTextChars": 0, + "retainedHeapBytes": 39672, + "afterClearHeapBytes": 15064 + }, + { + "count": 32, + "size": 65536, + "ingress": "json", + "retention": "live", + "phase": "before", + "visibleTextChars": 16384, + "retainedHeapBytes": 2125672, + "afterClearHeapBytes": -32 + }, + { + "count": 32, + "size": 65536, + "ingress": "json", + "retention": "live", + "phase": "after", + "visibleTextChars": 16384, + "retainedHeapBytes": 43536, + "afterClearHeapBytes": 544 + }, + { + "count": 32, + "size": 65536, + "ingress": "json", + "retention": "settled", + "phase": "before", + "visibleTextChars": 16384, + "retainedHeapBytes": 2127072, + "afterClearHeapBytes": 1424 + }, + { + "count": 32, + "size": 65536, + "ingress": "json", + "retention": "settled", + "phase": "after", + "visibleTextChars": 16384, + "retainedHeapBytes": 44296, + "afterClearHeapBytes": 1248 + }, + { + "count": 32, + "size": 65536, + "ingress": "json", + "retention": "removed", + "phase": "before", + "visibleTextChars": 0, + "retainedHeapBytes": 2108136, + "afterClearHeapBytes": 1072 + }, + { + "count": 32, + "size": 65536, + "ingress": "json", + "retention": "removed", + "phase": "after", + "visibleTextChars": 0, + "retainedHeapBytes": 25360, + "afterClearHeapBytes": 8832 + }, + { + "count": 8, + "size": 4194304, + "ingress": "flat", + "retention": "live", + "phase": "before", + "visibleTextChars": 4096, + "retainedHeapBytes": 33562624, + "afterClearHeapBytes": -320 + }, + { + "count": 8, + "size": 4194304, + "ingress": "flat", + "retention": "live", + "phase": "after", + "visibleTextChars": 4096, + "retainedHeapBytes": 11048, + "afterClearHeapBytes": -320 + }, + { + "count": 8, + "size": 4194304, + "ingress": "flat", + "retention": "settled", + "phase": "before", + "visibleTextChars": 4096, + "retainedHeapBytes": 33563960, + "afterClearHeapBytes": 912 + }, + { + "count": 8, + "size": 4194304, + "ingress": "flat", + "retention": "settled", + "phase": "after", + "visibleTextChars": 4096, + "retainedHeapBytes": 12384, + "afterClearHeapBytes": 464 + }, + { + "count": 8, + "size": 4194304, + "ingress": "flat", + "retention": "removed", + "phase": "before", + "visibleTextChars": 0, + "retainedHeapBytes": 33559744, + "afterClearHeapBytes": 1112 + }, + { + "count": 8, + "size": 4194304, + "ingress": "flat", + "retention": "removed", + "phase": "after", + "visibleTextChars": 0, + "retainedHeapBytes": 7512, + "afterClearHeapBytes": 552 + }, + { + "count": 8, + "size": 4194304, + "ingress": "cons", + "retention": "live", + "phase": "before", + "visibleTextChars": 4096, + "retainedHeapBytes": 33562624, + "afterClearHeapBytes": -384 + }, + { + "count": 8, + "size": 4194304, + "ingress": "cons", + "retention": "live", + "phase": "after", + "visibleTextChars": 4096, + "retainedHeapBytes": 11048, + "afterClearHeapBytes": -384 + }, + { + "count": 8, + "size": 4194304, + "ingress": "cons", + "retention": "settled", + "phase": "before", + "visibleTextChars": 4096, + "retainedHeapBytes": 33563960, + "afterClearHeapBytes": 784 + }, + { + "count": 8, + "size": 4194304, + "ingress": "cons", + "retention": "settled", + "phase": "after", + "visibleTextChars": 4096, + "retainedHeapBytes": 12384, + "afterClearHeapBytes": 416 + }, + { + "count": 8, + "size": 4194304, + "ingress": "cons", + "retention": "removed", + "phase": "before", + "visibleTextChars": 0, + "retainedHeapBytes": 32432128, + "afterClearHeapBytes": -1126504 + }, + { + "count": 8, + "size": 4194304, + "ingress": "cons", + "retention": "removed", + "phase": "after", + "visibleTextChars": 0, + "retainedHeapBytes": 7008, + "afterClearHeapBytes": 48 + }, + { + "count": 8, + "size": 4194304, + "ingress": "json", + "retention": "live", + "phase": "before", + "visibleTextChars": 4096, + "retainedHeapBytes": 33562624, + "afterClearHeapBytes": -384 + }, + { + "count": 8, + "size": 4194304, + "ingress": "json", + "retention": "live", + "phase": "after", + "visibleTextChars": 4096, + "retainedHeapBytes": 11048, + "afterClearHeapBytes": -384 + }, + { + "count": 8, + "size": 4194304, + "ingress": "json", + "retention": "settled", + "phase": "before", + "visibleTextChars": 4096, + "retainedHeapBytes": 33563960, + "afterClearHeapBytes": 432 + }, + { + "count": 8, + "size": 4194304, + "ingress": "json", + "retention": "settled", + "phase": "after", + "visibleTextChars": 4096, + "retainedHeapBytes": 11656, + "afterClearHeapBytes": -392 + }, + { + "count": 8, + "size": 4194304, + "ingress": "json", + "retention": "removed", + "phase": "before", + "visibleTextChars": 0, + "retainedHeapBytes": 33558584, + "afterClearHeapBytes": -48 + }, + { + "count": 8, + "size": 4194304, + "ingress": "json", + "retention": "removed", + "phase": "after", + "visibleTextChars": 0, + "retainedHeapBytes": 7008, + "afterClearHeapBytes": 48 + } + ] +} diff --git a/docs/audits/osc133-carry-retention/README.md b/docs/audits/osc133-carry-retention/README.md new file mode 100644 index 00000000000..e49a2aea734 --- /dev/null +++ b/docs/audits/osc133-carry-retention/README.md @@ -0,0 +1,106 @@ +# Retained OSC 133 incomplete carry + +The shared command-lifecycle scanner keeps an incomplete OSC 133 suffix of at +most 4,096 UTF-16 code units. A V8 sliced string can keep the entire preceding +PTY input alive through that small suffix. The correction copies only the final +incomplete carry through existing `ownRetainedString`; short prefixes, content, +parsing, callbacks, authority and reset behavior are preserved. + +This adds the fifteenth retained-text boundary to +[#20960](https://github.com/stablyai/orca/pull/20960), following the +[kitty/mouse tails](../terminal-mode-tail-retention/README.md) and other +[retained text slices](../retained-text-slices/README.md). It introduces no wire +change and applies equally to local and SSH/remote terminal bytes reaching the +shared scanner. + +## Reproduce + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/osc133-carry-retention/reproduce.cjs +``` + +Run the same script with the installed Electron executable, setting +`ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1`, with the same Node flags. +No application window, native PTY, or network is created. The runner has a +60-second deadline and accepts an optional output-report path as its first +argument; otherwise it writes [Node](./node-results.json) or +[Electron](./electron-results.json) results here. + +The portable loader validates all 28 bundled source modules and seven additional +caller/fixture files. Non-evaluated provenance callers accept only the recorded +audited or named-main bytes, and reports identify which was present; evaluated +modules each require one exact fixed hash. It reverses only the new import/copy call in memory through +a zero-context [patch](./fix.patch), then validates the baseline hash. All +evaluated-source and artifact hashes are recorded. It needs no Git history, +ignored notes, or absolute developer paths. Source/patch reads normalize CRLF; +an in-memory CRLF control checks equivalent before/after strings. + +The scanner baseline exactly matches named main +`291b4ddd6f1c1af480169885e0fda7f9c78ff053` and `v1.4.198` +(`e0826956fcfc532f5a1e55b5e081f2e57e553c43`). The copier did not exist in +`v1.4.198`; current helpers and callers are used for both sides of this +experiment. [Source provenance](./source-versions.json) records each named +identity/absence separately. This is not a replay of a complete historical app. + +## Result and controls + +Both Node 26.6 and Electron 43.7 / Node 24.21 pass **117 cases**. Each runtime +compares the baseline, fixed Buffer copier and fixed Bufferless copier through +the actual scanner, shared title tracker, and daemon background transient-fact +relay. Thirty-two 64 Ki-character inputs retain roughly 2 MiB before the copy; +eight 1 Mi-character inputs retain roughly 8 MiB. Fixed deltas are below the +asserted 1 MiB tolerance, including owner overhead. Completion and reset/exit +release the old parents. Exact GC-sensitive measurements are in the reports; +they are heap deltas, not RSS or exact allocation attribution. + +The ordinary sequence bytes come from the fish 4.7.1 capture documented in +`src/shared/terminal-mode-2031-final-state.test.ts`: `A;click_events=1` and +`C;cmdline_url=npx`. That capture contains complete OSC sequences. **The large +plain-output prefix and cut before the terminator are synthetic.** This does +not claim the original capture had those sizes or boundaries. + +Controls preserve BEL/ST completion, split prefixes, C/D callback values, +background disable/re-enable, reset, and every split of a Unicode/NUL/lone- +surrogate fixture. The Bufferless copier is selected while Buffer is absent, +then Buffer is restored before measurement; this exercises the renderer's +actual fallback without launching a renderer. Short ordinary `D;0` prefixes, +complete sequences and plain input are negative retention controls. Oversized +unterminated input is separately labelled malformed-protocol stress. V8's +independent last successful RegExp input is reset before both measurements. + +Eight permanent tests cover both copier paths, long captured-fish suffixes, +completion, reset and short `D;0`. With the in-memory baseline overlay, exactly +four long-suffix regressions fail at 33,550,680–33,565,360 retained bytes against +a 2 MiB allowance; the other 41 tests in the four-suite run pass. The fixed run +passes all 45. Wider proof/quality validation is recorded in +[validation.json](./validation.json). + +## Owners and ordinary input bounds + +Main creates a per-PTY tracker with `onCommandFinished` in +`orca-runtime-get-unpersisted-tracked-title-for-pty.ts`; scanner enablement still +respects transient-fact consumer/authority state. Ordinary daemon output frames +delivered to main are sliced to 64 Ki characters in +`daemon-stream-data-batcher.ts`, and ordinary relay output to 16 Ki characters +in `src/relay/pty-handler.ts`. The 64 Ki cases therefore demonstrate retention +without requiring a multi-megabyte main input; the 1 Mi cases amplify the +mechanism. Replay and transformed output have their own existing limits. + +The daemon's `BackgroundTransientFactRelay` owns one tracker per background +session. `daemon-terminal-admission.ts` feeds it before output batching, so the +batcher's later slicing is not an input cap on this daemon scanner. Native data +passes through `pty-subprocess/subprocess-handle.ts`, the session's shell +readiness/startup/recovery path, and its stream client. The inspected local +intake does not impose an independent string-length limit; platform/native +library chunk sizes were not measured here. + +Completion/replacement of the incomplete escape, scanner reset, session exit, +background retirement, tracker disposal or owner release drops the old parent. +This is at most the last incomplete-parent cost per live scanner, not a list of +every historical chunk. Multiple readers may share the same input backing +storage; do not add their isolated measurements as independent process totals. + +This is a reproduced code-level retention mechanism. It does not establish a +native output pause, normal-session frequency/duration, the trigger in +#19831/#19768, a reported sustained growth rate, or a multi-gigabyte incident's +cause. The change does not reduce original input allocation. diff --git a/docs/audits/osc133-carry-retention/before.config.mjs b/docs/audits/osc133-carry-retention/before.config.mjs new file mode 100644 index 00000000000..c1ca6c982ba --- /dev/null +++ b/docs/audits/osc133-carry-retention/before.config.mjs @@ -0,0 +1,23 @@ +import { resolve } from 'node:path' +import { createRequire } from 'node:module' +import { defineConfig, mergeConfig } from 'vitest/config' +import baseConfig from '../../../config/vitest.config.ts' + +const { loadSources, versions } = createRequire(import.meta.url)('./sources.cjs') +const { baseline } = loadSources() +const target = resolve(versions.sourcePath) + +export default mergeConfig( + baseConfig, + defineConfig({ + plugins: [ + { + name: 'osc133-before-owned-carry', + enforce: 'pre', + transform(_source, id) { + return resolve(id.split('?')[0]) === target ? { code: baseline, map: null } : undefined + } + } + ] + }) +) diff --git a/docs/audits/osc133-carry-retention/electron-results.json b/docs/audits/osc133-carry-retention/electron-results.json new file mode 100644 index 00000000000..f515770b5ec --- /dev/null +++ b/docs/audits/osc133-carry-retention/electron-results.json @@ -0,0 +1,1535 @@ +{ + "scope": "Actual-source bounded diagnostic; captured fish sequence syntax with synthetic large-prefix placement and chunk split. No affected-host, RSS, native PTY, or whole-release claim.", + "node": "v24.21.0", + "electron": "43.7.0", + "v8": "15.0.245.31-electron.0", + "sourcePath": "src/shared/terminal-osc133-command-finished.ts", + "crlfReads": 2, + "artifactHashes": { + "sources.cjs": "90404793218081e62aabd7649ce83fb1ec026e3c3bf95f056a190c7ef1cd0c08", + "scenario.cjs": "f3ebcfe65d68d05e392803161acac38a5958a69d7f84e3943a0cc293dd73121e", + "reproduce.cjs": "b84497f3bc4b9541347b84fa3d2fac57289519a329287d67a44e5fad6d35ac3d", + "source-versions.json": "3ad70d2c35b97b01ff729146240882d3a8b1c5d06989236e88b3574b5aec2f70", + "fix.patch": "cd48c5d6fd32fc5e7fcdc682a9b92c875ba5f08d975e8330f495e83d1377bf64", + "before.config.mjs": "593fb80a8506cfc226b0663964fb7690ceccf9d641c91230d923823bde291788" + }, + "versions": [ + { + "variant": "baseline", + "fixed": false, + "sourceSha256": "1790083b5200d11e68257556dffb49d2e8700841dccc6ccbbaa2c30b11b1efcd", + "bundleSha256": "994f6a288ddd6e16990410baa0304d4b1b240ff544978a8441321cd238f09306", + "evaluatedSources": { + "src/shared/terminal-output-side-effects.ts": "f609970e2ea7e53fc9e1ab0e0caca038b910dee3c092a9297377689871845f49", + "src/main/daemon/daemon-background-transient-facts.ts": "6be5c76683a0beb46a205d2f3d343f4e5570ff61b330b73213c72b62a7e7c5b2", + "src/shared/terminal-osc133-command-finished.ts": "1790083b5200d11e68257556dffb49d2e8700841dccc6ccbbaa2c30b11b1efcd", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/terminal-github-pr-link-detector.ts": "7c9509c50cfe733b5f9b29bcbd5c684825b3c18b2fac82275bd9047e7026100e", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/terminal-bell-detector.ts": "fffa4e0717222ea3059b4aab06a3403b6c972398d5fa9e4acf3316159d18dc05", + "src/shared/terminal-color-scheme-protocol.ts": "b8d1ee7693ae5053a8e3c9c099b8ab81032aed1f9a984de6eaad59e8c10c1700", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/github/links.ts": "3a7bdfabed6fa37272df8c60c6933bf1bb3b783d7ca4adddf9c8d4c579295da7", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922" + }, + "callerSourceHashes": { + "src/main/daemon/daemon-stream-data-batcher.ts": "56d4e6b8b461d45791ce1d4cc2185109554ecc57cc3163e68ad43c3001e91fbe", + "src/main/daemon/daemon-terminal-admission.ts": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "src/main/daemon/pty-subprocess/subprocess-handle.ts": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a", + "src/main/daemon/session.ts": "ddd71285f6e72bcee012e7a9955170be4fb59c8e41e70971bc629c13504a4338", + "src/main/runtime/orca-runtime-apply-tracked-pty-title.ts": "3c2ba07ff330875c5a9a07e44c0ffbcb1ebee1ae4af1952502ec733d6c7f7bea", + "src/main/runtime/orca-runtime-get-unpersisted-tracked-title-for-pty.ts": "107666bcc7e4bc744db51e9a023d659425947b134b9f420a0f11fb9ff0a7ba33", + "src/shared/terminal-mode-2031-final-state.test.ts": "42f3ff8839969f36a98f05c36d93abb7da291a9c3d2923a185a4933f4f245e10" + }, + "controls": { + "emitted": [["started"], ["finished", 137], ["finished", 0], ["finished", null]], + "facts": [ + [ + "s", + { + "kind": "command-finished", + "exitCode": 137 + } + ], + [ + "s", + { + "kind": "bell" + } + ] + ], + "splitResults": [ + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890] + ] + } + }, + { + "variant": "candidate-buffer", + "fixed": true, + "sourceSha256": "92bc1aa5eb975672c3b8761cce0da37d7ff6263c8f38c69d0715f73bd8e075d0", + "bundleSha256": "43064795284df44470398f46a53dc21489dcabe9db0bbb1c3f087a1580767b75", + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/main/daemon/daemon-background-transient-facts.ts": "6be5c76683a0beb46a205d2f3d343f4e5570ff61b330b73213c72b62a7e7c5b2", + "src/shared/terminal-output-side-effects.ts": "f609970e2ea7e53fc9e1ab0e0caca038b910dee3c092a9297377689871845f49", + "src/shared/terminal-osc133-command-finished.ts": "92bc1aa5eb975672c3b8761cce0da37d7ff6263c8f38c69d0715f73bd8e075d0", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/terminal-color-scheme-protocol.ts": "b8d1ee7693ae5053a8e3c9c099b8ab81032aed1f9a984de6eaad59e8c10c1700", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/terminal-github-pr-link-detector.ts": "7c9509c50cfe733b5f9b29bcbd5c684825b3c18b2fac82275bd9047e7026100e", + "src/shared/terminal-bell-detector.ts": "fffa4e0717222ea3059b4aab06a3403b6c972398d5fa9e4acf3316159d18dc05", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/github/links.ts": "3a7bdfabed6fa37272df8c60c6933bf1bb3b783d7ca4adddf9c8d4c579295da7", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922" + }, + "callerSourceHashes": { + "src/main/daemon/daemon-stream-data-batcher.ts": "56d4e6b8b461d45791ce1d4cc2185109554ecc57cc3163e68ad43c3001e91fbe", + "src/main/daemon/daemon-terminal-admission.ts": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "src/main/daemon/pty-subprocess/subprocess-handle.ts": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a", + "src/main/daemon/session.ts": "ddd71285f6e72bcee012e7a9955170be4fb59c8e41e70971bc629c13504a4338", + "src/main/runtime/orca-runtime-apply-tracked-pty-title.ts": "3c2ba07ff330875c5a9a07e44c0ffbcb1ebee1ae4af1952502ec733d6c7f7bea", + "src/main/runtime/orca-runtime-get-unpersisted-tracked-title-for-pty.ts": "107666bcc7e4bc744db51e9a023d659425947b134b9f420a0f11fb9ff0a7ba33", + "src/shared/terminal-mode-2031-final-state.test.ts": "42f3ff8839969f36a98f05c36d93abb7da291a9c3d2923a185a4933f4f245e10" + }, + "controls": { + "emitted": [["started"], ["finished", 137], ["finished", 0], ["finished", null]], + "facts": [ + [ + "s", + { + "kind": "command-finished", + "exitCode": 137 + } + ], + [ + "s", + { + "kind": "bell" + } + ] + ], + "splitResults": [ + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890] + ] + } + }, + { + "variant": "candidate-fallback", + "fixed": true, + "sourceSha256": "92bc1aa5eb975672c3b8761cce0da37d7ff6263c8f38c69d0715f73bd8e075d0", + "bundleSha256": "43064795284df44470398f46a53dc21489dcabe9db0bbb1c3f087a1580767b75", + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/terminal-osc133-command-finished.ts": "92bc1aa5eb975672c3b8761cce0da37d7ff6263c8f38c69d0715f73bd8e075d0", + "src/main/daemon/daemon-background-transient-facts.ts": "6be5c76683a0beb46a205d2f3d343f4e5570ff61b330b73213c72b62a7e7c5b2", + "src/shared/terminal-output-side-effects.ts": "f609970e2ea7e53fc9e1ab0e0caca038b910dee3c092a9297377689871845f49", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/terminal-color-scheme-protocol.ts": "b8d1ee7693ae5053a8e3c9c099b8ab81032aed1f9a984de6eaad59e8c10c1700", + "src/shared/terminal-github-pr-link-detector.ts": "7c9509c50cfe733b5f9b29bcbd5c684825b3c18b2fac82275bd9047e7026100e", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/terminal-bell-detector.ts": "fffa4e0717222ea3059b4aab06a3403b6c972398d5fa9e4acf3316159d18dc05", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/github/links.ts": "3a7bdfabed6fa37272df8c60c6933bf1bb3b783d7ca4adddf9c8d4c579295da7", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922" + }, + "callerSourceHashes": { + "src/main/daemon/daemon-stream-data-batcher.ts": "56d4e6b8b461d45791ce1d4cc2185109554ecc57cc3163e68ad43c3001e91fbe", + "src/main/daemon/daemon-terminal-admission.ts": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "src/main/daemon/pty-subprocess/subprocess-handle.ts": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a", + "src/main/daemon/session.ts": "ddd71285f6e72bcee012e7a9955170be4fb59c8e41e70971bc629c13504a4338", + "src/main/runtime/orca-runtime-apply-tracked-pty-title.ts": "3c2ba07ff330875c5a9a07e44c0ffbcb1ebee1ae4af1952502ec733d6c7f7bea", + "src/main/runtime/orca-runtime-get-unpersisted-tracked-title-for-pty.ts": "107666bcc7e4bc744db51e9a023d659425947b134b9f420a0f11fb9ff0a7ba33", + "src/shared/terminal-mode-2031-final-state.test.ts": "42f3ff8839969f36a98f05c36d93abb7da291a9c3d2923a185a4933f4f245e10" + }, + "controls": { + "emitted": [["started"], ["finished", 137], ["finished", 0], ["finished", null]], + "facts": [ + [ + "s", + { + "kind": "command-finished", + "exitCode": 137 + } + ], + [ + "s", + { + "kind": "bell" + } + ] + ], + "splitResults": [ + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890] + ] + } + } + ], + "reports": [ + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 2119572, + "completedDelta": 22604 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": 8382328, + "completedDelta": 26640 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 2105272, + "completedDelta": 7248 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": 8382472, + "completedDelta": -5888 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 7876, + "completedDelta": 7252 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": -6344, + "completedDelta": -6536 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 8032, + "completedDelta": 8064 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": -6536, + "completedDelta": -6536 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 7160, + "completedDelta": 7160 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": -6536, + "completedDelta": -6536 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 2105312, + "completedDelta": 7136 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 8382328, + "completedDelta": -6536 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "reset-without-completion", + "resetDelta": 2512 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 2127668, + "completedDelta": 29824 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": 8372484, + "completedDelta": -16380 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 2115972, + "completedDelta": 17796 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": 8372484, + "completedDelta": -16380 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 19708, + "completedDelta": 19060 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": -16188, + "completedDelta": -16380 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 17796, + "completedDelta": 17796 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": -16380, + "completedDelta": -16380 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 20928, + "completedDelta": 20928 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": -16380, + "completedDelta": -16380 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 2125312, + "completedDelta": 27136 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 8374304, + "completedDelta": -14560 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "reset-without-completion", + "resetDelta": 3512 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 2167588, + "completedDelta": 69168 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": 8394340, + "completedDelta": 5316 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 2151636, + "completedDelta": 52820 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": 8395036, + "completedDelta": 6012 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 46472, + "completedDelta": 48232 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": 5608, + "completedDelta": 5256 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 45640, + "completedDelta": 49600 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": 5416, + "completedDelta": 5256 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 39900, + "completedDelta": 45920 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": 84, + "completedDelta": 84 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 2144416, + "completedDelta": 45600 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 8394880, + "completedDelta": 5856 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "reset-without-completion", + "resetDelta": -13700 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 7620, + "completedDelta": 8732 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": -6248, + "completedDelta": -6536 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 8248, + "completedDelta": 7096 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": -6248, + "completedDelta": -6536 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 7864, + "completedDelta": 8100 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": -6344, + "completedDelta": -6536 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 7096, + "completedDelta": 7128 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": -6536, + "completedDelta": -6536 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 7160, + "completedDelta": 7160 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": -6536, + "completedDelta": -6536 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 138568, + "completedDelta": 7112 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 26328, + "completedDelta": -6536 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "reset-without-completion", + "resetDelta": -592 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 34752, + "completedDelta": 33664 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": -16164, + "completedDelta": -16452 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 18948, + "completedDelta": 17796 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": -16092, + "completedDelta": -16380 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 18564, + "completedDelta": 17796 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": -16188, + "completedDelta": -16380 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 17796, + "completedDelta": 17796 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": -16380, + "completedDelta": -16380 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 20884, + "completedDelta": 20884 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": -16380, + "completedDelta": -16380 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 168044, + "completedDelta": 36588 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 18248, + "completedDelta": -14616 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "reset-without-completion", + "resetDelta": 3424 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 61964, + "completedDelta": 60356 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": 5704, + "completedDelta": 5256 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 54568, + "completedDelta": 52776 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": 5704, + "completedDelta": 5256 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 46472, + "completedDelta": 45168 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": 5608, + "completedDelta": 5256 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 45640, + "completedDelta": 49604 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": 5416, + "completedDelta": 5256 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 39900, + "completedDelta": 39900 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": 140, + "completedDelta": 140 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 177696, + "completedDelta": 45600 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 38880, + "completedDelta": 5856 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "reset-without-completion", + "resetDelta": -14268 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 9712, + "completedDelta": 9332 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": -6248, + "completedDelta": -6536 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 8248, + "completedDelta": 7096 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": -6248, + "completedDelta": -6536 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 7864, + "completedDelta": 7096 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": -6344, + "completedDelta": -6536 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 7096, + "completedDelta": 7096 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": -6536, + "completedDelta": -6536 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 7128, + "completedDelta": 7128 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": -6536, + "completedDelta": -6536 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 147476, + "completedDelta": 16020 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 26328, + "completedDelta": -6536 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "reset-without-completion", + "resetDelta": -592 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 22252, + "completedDelta": 22168 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": -16092, + "completedDelta": -16380 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 18948, + "completedDelta": 17796 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": -16092, + "completedDelta": -16380 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 18564, + "completedDelta": 17796 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": -16188, + "completedDelta": -16380 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 17796, + "completedDelta": 17796 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": -16380, + "completedDelta": -16380 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 20844, + "completedDelta": 20844 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": -10132, + "completedDelta": -10132 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 164608, + "completedDelta": 27656 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 16484, + "completedDelta": -16380 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "reset-without-completion", + "resetDelta": 1884 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 53476, + "completedDelta": 51684 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": 5704, + "completedDelta": 7968 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 46876, + "completedDelta": 45084 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": 5704, + "completedDelta": 5256 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 46440, + "completedDelta": 45080 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": 5608, + "completedDelta": 5256 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 50212, + "completedDelta": 49616 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": 5416, + "completedDelta": 5256 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 39900, + "completedDelta": 39900 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": 140, + "completedDelta": 140 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 177696, + "completedDelta": 45600 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 38880, + "completedDelta": 5856 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "reset-without-completion", + "resetDelta": -25044 + } + ] +} diff --git a/docs/audits/osc133-carry-retention/fix.patch b/docs/audits/osc133-carry-retention/fix.patch new file mode 100644 index 00000000000..06455a072aa --- /dev/null +++ b/docs/audits/osc133-carry-retention/fix.patch @@ -0,0 +1,7 @@ +--- a/src/shared/terminal-osc133-command-finished.ts ++++ b/src/shared/terminal-osc133-command-finished.ts +@@ -9,0 +10,2 @@ ++ ++import { ownRetainedString } from './own-retained-string' +@@ -93,0 +96 @@ ++ carry = ownRetainedString(carry) diff --git a/docs/audits/osc133-carry-retention/node-results.json b/docs/audits/osc133-carry-retention/node-results.json new file mode 100644 index 00000000000..a6ffeda453e --- /dev/null +++ b/docs/audits/osc133-carry-retention/node-results.json @@ -0,0 +1,1535 @@ +{ + "scope": "Actual-source bounded diagnostic; captured fish sequence syntax with synthetic large-prefix placement and chunk split. No affected-host, RSS, native PTY, or whole-release claim.", + "node": "v26.6.0", + "electron": null, + "v8": "14.6.202.34-node.26", + "sourcePath": "src/shared/terminal-osc133-command-finished.ts", + "crlfReads": 2, + "artifactHashes": { + "sources.cjs": "90404793218081e62aabd7649ce83fb1ec026e3c3bf95f056a190c7ef1cd0c08", + "scenario.cjs": "f3ebcfe65d68d05e392803161acac38a5958a69d7f84e3943a0cc293dd73121e", + "reproduce.cjs": "b84497f3bc4b9541347b84fa3d2fac57289519a329287d67a44e5fad6d35ac3d", + "source-versions.json": "3ad70d2c35b97b01ff729146240882d3a8b1c5d06989236e88b3574b5aec2f70", + "fix.patch": "cd48c5d6fd32fc5e7fcdc682a9b92c875ba5f08d975e8330f495e83d1377bf64", + "before.config.mjs": "593fb80a8506cfc226b0663964fb7690ceccf9d641c91230d923823bde291788" + }, + "versions": [ + { + "variant": "baseline", + "fixed": false, + "sourceSha256": "1790083b5200d11e68257556dffb49d2e8700841dccc6ccbbaa2c30b11b1efcd", + "bundleSha256": "994f6a288ddd6e16990410baa0304d4b1b240ff544978a8441321cd238f09306", + "evaluatedSources": { + "src/main/daemon/daemon-background-transient-facts.ts": "6be5c76683a0beb46a205d2f3d343f4e5570ff61b330b73213c72b62a7e7c5b2", + "src/shared/terminal-osc133-command-finished.ts": "1790083b5200d11e68257556dffb49d2e8700841dccc6ccbbaa2c30b11b1efcd", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/terminal-output-side-effects.ts": "f609970e2ea7e53fc9e1ab0e0caca038b910dee3c092a9297377689871845f49", + "src/shared/terminal-color-scheme-protocol.ts": "b8d1ee7693ae5053a8e3c9c099b8ab81032aed1f9a984de6eaad59e8c10c1700", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/terminal-github-pr-link-detector.ts": "7c9509c50cfe733b5f9b29bcbd5c684825b3c18b2fac82275bd9047e7026100e", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/terminal-bell-detector.ts": "fffa4e0717222ea3059b4aab06a3403b6c972398d5fa9e4acf3316159d18dc05", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/github/links.ts": "3a7bdfabed6fa37272df8c60c6933bf1bb3b783d7ca4adddf9c8d4c579295da7", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922" + }, + "callerSourceHashes": { + "src/main/daemon/daemon-stream-data-batcher.ts": "56d4e6b8b461d45791ce1d4cc2185109554ecc57cc3163e68ad43c3001e91fbe", + "src/main/daemon/daemon-terminal-admission.ts": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "src/main/daemon/pty-subprocess/subprocess-handle.ts": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a", + "src/main/daemon/session.ts": "ddd71285f6e72bcee012e7a9955170be4fb59c8e41e70971bc629c13504a4338", + "src/main/runtime/orca-runtime-apply-tracked-pty-title.ts": "3c2ba07ff330875c5a9a07e44c0ffbcb1ebee1ae4af1952502ec733d6c7f7bea", + "src/main/runtime/orca-runtime-get-unpersisted-tracked-title-for-pty.ts": "107666bcc7e4bc744db51e9a023d659425947b134b9f420a0f11fb9ff0a7ba33", + "src/shared/terminal-mode-2031-final-state.test.ts": "42f3ff8839969f36a98f05c36d93abb7da291a9c3d2923a185a4933f4f245e10" + }, + "controls": { + "emitted": [["started"], ["finished", 137], ["finished", 0], ["finished", null]], + "facts": [ + [ + "s", + { + "kind": "command-finished", + "exitCode": 137 + } + ], + [ + "s", + { + "kind": "bell" + } + ] + ], + "splitResults": [ + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890] + ] + } + }, + { + "variant": "candidate-buffer", + "fixed": true, + "sourceSha256": "92bc1aa5eb975672c3b8761cce0da37d7ff6263c8f38c69d0715f73bd8e075d0", + "bundleSha256": "43064795284df44470398f46a53dc21489dcabe9db0bbb1c3f087a1580767b75", + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/terminal-osc133-command-finished.ts": "92bc1aa5eb975672c3b8761cce0da37d7ff6263c8f38c69d0715f73bd8e075d0", + "src/main/daemon/daemon-background-transient-facts.ts": "6be5c76683a0beb46a205d2f3d343f4e5570ff61b330b73213c72b62a7e7c5b2", + "src/shared/terminal-output-side-effects.ts": "f609970e2ea7e53fc9e1ab0e0caca038b910dee3c092a9297377689871845f49", + "src/shared/terminal-github-pr-link-detector.ts": "7c9509c50cfe733b5f9b29bcbd5c684825b3c18b2fac82275bd9047e7026100e", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/terminal-bell-detector.ts": "fffa4e0717222ea3059b4aab06a3403b6c972398d5fa9e4acf3316159d18dc05", + "src/shared/terminal-color-scheme-protocol.ts": "b8d1ee7693ae5053a8e3c9c099b8ab81032aed1f9a984de6eaad59e8c10c1700", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/github/links.ts": "3a7bdfabed6fa37272df8c60c6933bf1bb3b783d7ca4adddf9c8d4c579295da7", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922" + }, + "callerSourceHashes": { + "src/main/daemon/daemon-stream-data-batcher.ts": "56d4e6b8b461d45791ce1d4cc2185109554ecc57cc3163e68ad43c3001e91fbe", + "src/main/daemon/daemon-terminal-admission.ts": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "src/main/daemon/pty-subprocess/subprocess-handle.ts": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a", + "src/main/daemon/session.ts": "ddd71285f6e72bcee012e7a9955170be4fb59c8e41e70971bc629c13504a4338", + "src/main/runtime/orca-runtime-apply-tracked-pty-title.ts": "3c2ba07ff330875c5a9a07e44c0ffbcb1ebee1ae4af1952502ec733d6c7f7bea", + "src/main/runtime/orca-runtime-get-unpersisted-tracked-title-for-pty.ts": "107666bcc7e4bc744db51e9a023d659425947b134b9f420a0f11fb9ff0a7ba33", + "src/shared/terminal-mode-2031-final-state.test.ts": "42f3ff8839969f36a98f05c36d93abb7da291a9c3d2923a185a4933f4f245e10" + }, + "controls": { + "emitted": [["started"], ["finished", 137], ["finished", 0], ["finished", null]], + "facts": [ + [ + "s", + { + "kind": "command-finished", + "exitCode": 137 + } + ], + [ + "s", + { + "kind": "bell" + } + ] + ], + "splitResults": [ + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890] + ] + } + }, + { + "variant": "candidate-fallback", + "fixed": true, + "sourceSha256": "92bc1aa5eb975672c3b8761cce0da37d7ff6263c8f38c69d0715f73bd8e075d0", + "bundleSha256": "43064795284df44470398f46a53dc21489dcabe9db0bbb1c3f087a1580767b75", + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/terminal-output-side-effects.ts": "f609970e2ea7e53fc9e1ab0e0caca038b910dee3c092a9297377689871845f49", + "src/shared/terminal-osc133-command-finished.ts": "92bc1aa5eb975672c3b8761cce0da37d7ff6263c8f38c69d0715f73bd8e075d0", + "src/main/daemon/daemon-background-transient-facts.ts": "6be5c76683a0beb46a205d2f3d343f4e5570ff61b330b73213c72b62a7e7c5b2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/terminal-github-pr-link-detector.ts": "7c9509c50cfe733b5f9b29bcbd5c684825b3c18b2fac82275bd9047e7026100e", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/terminal-bell-detector.ts": "fffa4e0717222ea3059b4aab06a3403b6c972398d5fa9e4acf3316159d18dc05", + "src/shared/terminal-color-scheme-protocol.ts": "b8d1ee7693ae5053a8e3c9c099b8ab81032aed1f9a984de6eaad59e8c10c1700", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/github/links.ts": "3a7bdfabed6fa37272df8c60c6933bf1bb3b783d7ca4adddf9c8d4c579295da7", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922" + }, + "callerSourceHashes": { + "src/main/daemon/daemon-stream-data-batcher.ts": "56d4e6b8b461d45791ce1d4cc2185109554ecc57cc3163e68ad43c3001e91fbe", + "src/main/daemon/daemon-terminal-admission.ts": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "src/main/daemon/pty-subprocess/subprocess-handle.ts": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a", + "src/main/daemon/session.ts": "ddd71285f6e72bcee012e7a9955170be4fb59c8e41e70971bc629c13504a4338", + "src/main/runtime/orca-runtime-apply-tracked-pty-title.ts": "3c2ba07ff330875c5a9a07e44c0ffbcb1ebee1ae4af1952502ec733d6c7f7bea", + "src/main/runtime/orca-runtime-get-unpersisted-tracked-title-for-pty.ts": "107666bcc7e4bc744db51e9a023d659425947b134b9f420a0f11fb9ff0a7ba33", + "src/shared/terminal-mode-2031-final-state.test.ts": "42f3ff8839969f36a98f05c36d93abb7da291a9c3d2923a185a4933f4f245e10" + }, + "controls": { + "emitted": [["started"], ["finished", 137], ["finished", 0], ["finished", null]], + "facts": [ + [ + "s", + { + "kind": "command-finished", + "exitCode": 137 + } + ], + [ + "s", + { + "kind": "bell" + } + ] + ], + "splitResults": [ + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890], + [1234567890] + ] + } + } + ], + "reports": [ + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 2129640, + "completedDelta": 40400 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": 8376888, + "completedDelta": -12104 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 2112880, + "completedDelta": 14192 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": 8375904, + "completedDelta": -12464 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 15232, + "completedDelta": 14480 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": -12816, + "completedDelta": -13072 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 14192, + "completedDelta": 14480 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": -13072, + "completedDelta": -13072 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 14320, + "completedDelta": 14384 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": -12800, + "completedDelta": -11920 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 2112960, + "completedDelta": 14272 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 8375920, + "completedDelta": -13072 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "scanner", + "input": "reset-without-completion", + "resetDelta": 4984 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 2149040, + "completedDelta": 50976 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": 8356232, + "completedDelta": -32760 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 2134280, + "completedDelta": 35592 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": 8356232, + "completedDelta": -32760 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 38088, + "completedDelta": 37288 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": -32504, + "completedDelta": -32760 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 35592, + "completedDelta": 35592 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": -32760, + "completedDelta": -32760 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 38800, + "completedDelta": 38800 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": -32760, + "completedDelta": -32760 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 2144864, + "completedDelta": 46176 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 8358200, + "completedDelta": -30792 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "title-tracker", + "input": "reset-without-completion", + "resetDelta": 7008 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 2203896, + "completedDelta": 113936 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": 8395000, + "completedDelta": 5688 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 2189328, + "completedDelta": 91960 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": 8394872, + "completedDelta": 6464 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 91008, + "completedDelta": 89120 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": 6064, + "completedDelta": 5488 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 86064, + "completedDelta": 89528 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": 5808, + "completedDelta": 5488 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 79544, + "completedDelta": 80520 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": 216, + "completedDelta": 216 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 2185568, + "completedDelta": 85600 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 8395616, + "completedDelta": 6304 + }, + { + "variant": "baseline", + "fixed": false, + "ownerKind": "background-relay", + "input": "reset-without-completion", + "resetDelta": -18456 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 14216, + "completedDelta": 14392 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": -12752, + "completedDelta": -13072 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 15720, + "completedDelta": 14440 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": -12752, + "completedDelta": -13072 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 17312, + "completedDelta": 15656 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": -12816, + "completedDelta": -13072 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 14192, + "completedDelta": 14480 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": -13072, + "completedDelta": -13072 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 14320, + "completedDelta": 14320 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": -13072, + "completedDelta": -13072 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 145808, + "completedDelta": 14224 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 19824, + "completedDelta": -13072 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "scanner", + "input": "reset-without-completion", + "resetDelta": -1152 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 54632, + "completedDelta": 53480 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": -32440, + "completedDelta": -32760 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 36872, + "completedDelta": 35592 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": -32440, + "completedDelta": -32760 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 36616, + "completedDelta": 35592 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": -32504, + "completedDelta": -32760 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 35592, + "completedDelta": 35592 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": -32760, + "completedDelta": -32760 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 38712, + "completedDelta": 38712 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": -32760, + "completedDelta": -32760 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 187152, + "completedDelta": 55568 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 1944, + "completedDelta": -30952 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "title-tracker", + "input": "reset-without-completion", + "resetDelta": 6848 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 103504, + "completedDelta": 101840 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": 6208, + "completedDelta": 5568 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 91848, + "completedDelta": 91968 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": 6128, + "completedDelta": 5488 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 87216, + "completedDelta": 85104 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": 6064, + "completedDelta": 5488 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 86064, + "completedDelta": 89568 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": 5808, + "completedDelta": 5488 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 79544, + "completedDelta": 79544 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": 216, + "completedDelta": 216 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 218464, + "completedDelta": 85600 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 39520, + "completedDelta": 6304 + }, + { + "variant": "candidate-buffer", + "fixed": true, + "ownerKind": "background-relay", + "input": "reset-without-completion", + "resetDelta": -19024 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 16728, + "completedDelta": 16568 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": -12752, + "completedDelta": -13072 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 15472, + "completedDelta": 14192 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": -12752, + "completedDelta": -13072 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 15216, + "completedDelta": 14192 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": -12816, + "completedDelta": -13072 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 14192, + "completedDelta": 14192 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": -13072, + "completedDelta": -13072 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 14256, + "completedDelta": 14256 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": -13072, + "completedDelta": -13072 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 155680, + "completedDelta": 24096 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 19824, + "completedDelta": -13072 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "scanner", + "input": "reset-without-completion", + "resetDelta": -1152 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 43472, + "completedDelta": 43672 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": -32440, + "completedDelta": -32760 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 36872, + "completedDelta": 35592 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": -32440, + "completedDelta": -32760 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 36616, + "completedDelta": 35592 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": -32504, + "completedDelta": -32760 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 35592, + "completedDelta": 35592 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": -32760, + "completedDelta": -32760 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 38632, + "completedDelta": 38632 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": -25984, + "completedDelta": -25984 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 179200, + "completedDelta": 48312 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 136, + "completedDelta": -32760 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "title-tracker", + "input": "reset-without-completion", + "resetDelta": 4608 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 22, + "retainedDelta": 92824, + "completedDelta": 90264 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-prompt-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 22, + "retainedDelta": 6128, + "completedDelta": 8184 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 23, + "retainedDelta": 87512, + "completedDelta": 84952 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 23, + "retainedDelta": 6128, + "completedDelta": 5488 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "short-standard-finished-partial", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 9, + "retainedDelta": 87152, + "completedDelta": 84936 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "short-standard-finished-partial", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 9, + "retainedDelta": 6064, + "completedDelta": 5488 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-complete", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 24, + "retainedDelta": 90856, + "completedDelta": 89656 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "captured-fish-command-complete", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 24, + "retainedDelta": 5808, + "completedDelta": 5488 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "no-escape", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 15, + "retainedDelta": 79544, + "completedDelta": 79544 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "no-escape", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 15, + "retainedDelta": 216, + "completedDelta": 216 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 65536, + "owners": 32, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 218464, + "completedDelta": 85600 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "oversized-incomplete-protocol", + "inputCodeUnits": 1048576, + "owners": 8, + "inputSuffixCodeUnits": 5006, + "retainedDelta": 39520, + "completedDelta": 6304 + }, + { + "variant": "candidate-fallback", + "fixed": true, + "ownerKind": "background-relay", + "input": "reset-without-completion", + "resetDelta": -31584 + } + ] +} diff --git a/docs/audits/osc133-carry-retention/reproduce.cjs b/docs/audits/osc133-carry-retention/reproduce.cjs new file mode 100644 index 00000000000..faccd954dfc --- /dev/null +++ b/docs/audits/osc133-carry-retention/reproduce.cjs @@ -0,0 +1,142 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const { load, loadSources, readText, sha, versions: sourceVersions } = require('./sources.cjs') +const { inputs, heap, makeOwner, behavior } = require('./scenario.cjs') + +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function', 'Run with --expose-gc') + +async function main() { + const reports = [] + const versions = [] + for (const variant of ['baseline', 'candidate-buffer', 'candidate-fallback']) { + const fixed = variant !== 'baseline' + const loaded = await load(fixed) + loaded.api.resetOwnRetainedStringCopier() + const originalBuffer = globalThis.Buffer + try { + if (variant === 'candidate-fallback') { + globalThis.Buffer = undefined + } + assert.equal( + loaded.api.ownRetainedString('prefix-\ud800a\udfff\u0000漢-suffix'), + 'prefix-\ud800a\udfff\u0000漢-suffix' + ) + } finally { + globalThis.Buffer = originalBuffer + } + const controls = behavior(loaded.api) + versions.push({ + variant, + fixed, + sourceSha256: loaded.sourceSha256, + bundleSha256: loaded.bundleSha256, + evaluatedSources: loaded.evaluatedSources, + callerSourceHashes: loaded.callerSourceHashes, + controls + }) + for (const ownerKind of ['scanner', 'title-tracker', 'background-relay']) { + for (const input of inputs) { + for (const [chars, count] of [ + [64 * 1024, 32], + [1024 * 1024, 8] + ]) { + const before = await heap() + const owners = Array.from({ length: count }, (_, index) => + makeOwner(loaded.api, ownerKind, input, chars, index) + ) + const retainedDelta = (await heap()) - before + const expectParent = !fixed && input.retained + assert.ok( + expectParent ? retainedDelta > chars * count * 0.75 : retainedDelta < 1024 * 1024, + JSON.stringify({ fixed, ownerKind, input: input.name, retainedDelta }) + ) + for (const owner of owners) { + owner.complete() + } + const completedDelta = (await heap()) - before + assert.ok( + completedDelta < 1024 * 1024, + JSON.stringify({ fixed, ownerKind, input: input.name, completedDelta }) + ) + for (const owner of owners) { + owner.release() + } + reports.push({ + variant, + fixed, + ownerKind, + input: input.name, + inputCodeUnits: chars, + owners: count, + inputSuffixCodeUnits: input.suffix.length, + retainedDelta, + completedDelta + }) + } + } + const input = inputs[0] + const before = await heap() + const owners = Array.from({ length: 8 }, (_, index) => + makeOwner(loaded.api, ownerKind, input, 1024 * 1024, index) + ) + for (const owner of owners) { + owner.release() + } + const resetDelta = (await heap()) - before + assert.ok(resetDelta < 1024 * 1024, JSON.stringify({ fixed, ownerKind, resetDelta })) + reports.push({ variant, fixed, ownerKind, input: 'reset-without-completion', resetDelta }) + } + } + assert.deepEqual(versions[0].controls, versions[1].controls) + assert.deepEqual(versions[0].controls, versions[2].controls) + let crlfReads = 0 + const crlfSources = loadSources((file) => { + crlfReads += 1 + return readText(file).replaceAll('\n', '\r\n') + }) + assert.deepEqual(crlfSources, loadSources()) + assert.equal(crlfReads, 2) + const artifacts = [ + 'sources.cjs', + 'scenario.cjs', + 'reproduce.cjs', + 'source-versions.json', + 'fix.patch', + 'before.config.mjs' + ] + const artifactHashes = Object.fromEntries( + artifacts.map((file) => [file, sha(readText(path.join(__dirname, file)))]) + ) + const result = { + scope: + 'Actual-source bounded diagnostic; captured fish sequence syntax with synthetic large-prefix placement and chunk split. No affected-host, RSS, native PTY, or whole-release claim.', + node: process.version, + electron: process.versions.electron ?? null, + v8: process.versions.v8, + sourcePath: sourceVersions.sourcePath, + crlfReads, + artifactHashes, + versions, + reports + } + const resultPath = process.argv[2] + ? path.resolve(process.argv[2]) + : path.join( + __dirname, + process.versions.electron ? 'electron-results.json' : 'node-results.json' + ) + fs.writeFileSync(resultPath, `${JSON.stringify(result, null, 2)}\n`) + console.log( + JSON.stringify({ resultPath, cases: reports.length, variants: versions.map((x) => x.variant) }) + ) +} +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) +setTimeout(() => { + console.error('fixture deadline') + process.exit(2) +}, 60000).unref() diff --git a/docs/audits/osc133-carry-retention/scenario.cjs b/docs/audits/osc133-carry-retention/scenario.cjs new file mode 100644 index 00000000000..39922432fdd --- /dev/null +++ b/docs/audits/osc133-carry-retention/scenario.cjs @@ -0,0 +1,99 @@ +const assert = require('node:assert/strict') + +const inputs = [ + { name: 'captured-fish-prompt-partial', suffix: '\x1b]133;A;click_events=1', retained: true }, + { name: 'captured-fish-command-partial', suffix: '\x1b]133;C;cmdline_url=npx', retained: true }, + { name: 'short-standard-finished-partial', suffix: '\x1b]133;D;0', retained: false }, + { + name: 'captured-fish-command-complete', + suffix: '\x1b]133;C;cmdline_url=npx\x07', + retained: false + }, + { name: 'no-escape', suffix: 'ordinary output', retained: false }, + { name: 'oversized-incomplete-protocol', suffix: `\x1b]133;${'x'.repeat(5000)}`, retained: true } +] + +async function heap() { + ;/(?:)/.test('') + for (let round = 0; round < 4; round++) { + await new Promise((resolve) => setImmediate(resolve)) + global.gc() + } + return process.memoryUsage().heapUsed +} + +function makeOwner(api, ownerKind, input, chars, index) { + const prefix = `${index}:` + const data = prefix + 'x'.repeat(chars - prefix.length - input.suffix.length) + input.suffix + if (ownerKind === 'scanner') { + const scanner = api.createOsc133CommandFinishedScanner(() => {}) + scanner.scan(data) + return { complete: () => scanner.scan('\x07'), release: () => scanner.reset() } + } + if (ownerKind === 'title-tracker') { + const tracker = api.createTerminalTitleTracker({ onCommandFinished: () => {} }) + tracker.handleChunk(data, { titleScanData: '' }) + return { + complete: () => tracker.handleChunk('\x07', { titleScanData: '' }), + release: () => tracker.dispose() + } + } + const relay = new api.BackgroundTransientFactRelay(() => {}) + relay.setSessionBackground('fixture-session', true) + relay.onSessionData('fixture-session', data) + return { + complete: () => relay.onSessionData('fixture-session', '\x07'), + release: () => relay.onSessionExit('fixture-session') + } +} + +function behavior(api) { + const emitted = [] + const scanner = api.createOsc133CommandFinishedScanner( + (code) => emitted.push(['finished', code]), + () => emitted.push(['started']) + ) + for (const chunk of [ + '\x1b]133;A;click_events=1', + '\x07', + '\x1b]133;C;cmdline_url=npx', + '\x07', + '\x1b]133;D;13', + '7\x1b', + '\\', + '\x1b]133;D;0\x07', + '\x1b]133;D;not-a-number\x07' + ]) { + scanner.scan(chunk) + } + scanner.scan('\x1b]133;D;1234567890') + scanner.reset() + scanner.scan('\x07') + assert.deepEqual(emitted, [['started'], ['finished', 137], ['finished', 0], ['finished', null]]) + const facts = [] + const relay = new api.BackgroundTransientFactRelay((id, fact) => facts.push([id, fact])) + relay.setSessionBackground('s', true) + relay.onSessionData('s', '\x1b]133;D;137') + relay.onSessionData('s', '\x07') + relay.onSessionData('s', '\x1b]133;D;22') + relay.setSessionBackground('s', false) + relay.setSessionBackground('s', true) + relay.onSessionData('s', '\x07') + relay.dispose() + assert.deepEqual(facts[0], ['s', { kind: 'command-finished', exitCode: 137 }]) + assert.equal(facts.filter(([, fact]) => fact.kind === 'command-finished').length, 1) + const utf16 = '\x1b]133;D;1234567890;\ud800a\udfff\u0000漢' + assert.equal(api.ownRetainedString(utf16), utf16) + const splitResults = [] + for (let cut = 1; cut < utf16.length; cut++) { + const values = [] + const split = api.createOsc133CommandFinishedScanner((code) => values.push(code)) + split.scan(utf16.slice(0, cut)) + split.scan(`${utf16.slice(cut)}\x1b\\`) + assert.deepEqual(values, [1234567890]) + splitResults.push(values) + } + return { emitted, facts, splitResults } +} + +module.exports = { inputs, heap, makeOwner, behavior } diff --git a/docs/audits/osc133-carry-retention/source-versions.json b/docs/audits/osc133-carry-retention/source-versions.json new file mode 100644 index 00000000000..5475bbf6302 --- /dev/null +++ b/docs/audits/osc133-carry-retention/source-versions.json @@ -0,0 +1,646 @@ +{ + "sourcePath": "src/shared/terminal-osc133-command-finished.ts", + "baselineSha256": "1790083b5200d11e68257556dffb49d2e8700841dccc6ccbbaa2c30b11b1efcd", + "fixedSha256": "92bc1aa5eb975672c3b8761cce0da37d7ff6263c8f38c69d0715f73bd8e075d0", + "sourceHashLineEndings": "canonical LF", + "dependencies": { + "src/shared/terminal-osc133-command-finished.ts": "92bc1aa5eb975672c3b8761cce0da37d7ff6263c8f38c69d0715f73bd8e075d0", + "src/shared/agent-name-token-match.ts": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "src/shared/agent-title-decoration.ts": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "src/shared/pi-state-title-marker.ts": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "src/shared/pi-compatible-synthetic-title.ts": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "src/shared/terminal-title-classification-memo.ts": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "src/shared/terminal-title-wrapper-segments.ts": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "src/shared/agent-title-core.ts": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "src/shared/opencode-terminal-title.ts": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "src/shared/agent-title-identity.ts": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "src/shared/synthetic-agent-title.ts": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "src/shared/tui-agent-display-names.ts": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "src/shared/agent-title-evidence.ts": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "src/shared/pane-agent-evidence-sources.ts": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "src/shared/pane-agent-identity-adapter.ts": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "src/shared/terminal-title-agent-type.ts": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "src/shared/agent-title-status.ts": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "src/shared/osc-title-extraction.ts": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "src/shared/shell-process-detection.ts": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "src/shared/agent-detection.ts": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "src/shared/terminal-bell-detector.ts": "fffa4e0717222ea3059b4aab06a3403b6c972398d5fa9e4acf3316159d18dc05", + "src/shared/terminal-color-scheme-protocol.ts": "b8d1ee7693ae5053a8e3c9c099b8ab81032aed1f9a984de6eaad59e8c10c1700", + "src/shared/github/links.ts": "3a7bdfabed6fa37272df8c60c6933bf1bb3b783d7ca4adddf9c8d4c579295da7", + "src/shared/terminal-github-pr-link-detector.ts": "7c9509c50cfe733b5f9b29bcbd5c684825b3c18b2fac82275bd9047e7026100e", + "src/shared/terminal-output-side-effects.ts": "f609970e2ea7e53fc9e1ab0e0caca038b910dee3c092a9297377689871845f49", + "src/main/daemon/daemon-background-transient-facts.ts": "6be5c76683a0beb46a205d2f3d343f4e5570ff61b330b73213c72b62a7e7c5b2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2" + }, + "callerHashes": [ + { + "path": "src/main/daemon/daemon-stream-data-batcher.ts", + "sha256": "56d4e6b8b461d45791ce1d4cc2185109554ecc57cc3163e68ad43c3001e91fbe", + "acceptedSha256": [ + "56d4e6b8b461d45791ce1d4cc2185109554ecc57cc3163e68ad43c3001e91fbe", + "958a01e69c8e24f6eeebe44d0ba0e4e3fe50cb27e7c2302ee8582827633ee26f" + ] + }, + { + "path": "src/main/daemon/daemon-terminal-admission.ts", + "sha256": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "acceptedSha256": ["14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251"] + }, + { + "path": "src/main/daemon/pty-subprocess/subprocess-handle.ts", + "sha256": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a", + "acceptedSha256": [ + "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a", + "12ec9e8b5e5f7175310f9522c3f827b19b871c3df5b09e8b1b2c4fe1fc92ba4e" + ] + }, + { + "path": "src/main/daemon/session.ts", + "sha256": "ddd71285f6e72bcee012e7a9955170be4fb59c8e41e70971bc629c13504a4338", + "acceptedSha256": [ + "ddd71285f6e72bcee012e7a9955170be4fb59c8e41e70971bc629c13504a4338", + "fcf79c354e29bf73549b3fdd4d905d431c210699391f45916bd27dbe858f86b4" + ] + }, + { + "path": "src/main/runtime/orca-runtime-apply-tracked-pty-title.ts", + "sha256": "3c2ba07ff330875c5a9a07e44c0ffbcb1ebee1ae4af1952502ec733d6c7f7bea", + "acceptedSha256": ["3c2ba07ff330875c5a9a07e44c0ffbcb1ebee1ae4af1952502ec733d6c7f7bea"] + }, + { + "path": "src/main/runtime/orca-runtime-get-unpersisted-tracked-title-for-pty.ts", + "sha256": "107666bcc7e4bc744db51e9a023d659425947b134b9f420a0f11fb9ff0a7ba33", + "acceptedSha256": ["107666bcc7e4bc744db51e9a023d659425947b134b9f420a0f11fb9ff0a7ba33"] + }, + { + "path": "src/shared/terminal-mode-2031-final-state.test.ts", + "sha256": "42f3ff8839969f36a98f05c36d93abb7da291a9c3d2923a185a4933f4f245e10", + "acceptedSha256": ["42f3ff8839969f36a98f05c36d93abb7da291a9c3d2923a185a4933f4f245e10"] + } + ], + "namedSourceProvenance": [ + { + "path": "src/main/daemon/daemon-background-transient-facts.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "6be5c76683a0beb46a205d2f3d343f4e5570ff61b330b73213c72b62a7e7c5b2", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "6be5c76683a0beb46a205d2f3d343f4e5570ff61b330b73213c72b62a7e7c5b2", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "6be5c76683a0beb46a205d2f3d343f4e5570ff61b330b73213c72b62a7e7c5b2" + }, + { + "path": "src/main/daemon/daemon-stream-data-batcher.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "958a01e69c8e24f6eeebe44d0ba0e4e3fe50cb27e7c2302ee8582827633ee26f", + "matchesAuditedBefore": false + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "1c90541151cb3b4a2c77b731db1d2da9cf69f19ad3cd6e673555c7f652edc9ba", + "matchesAuditedBefore": false + } + }, + "auditedBeforeSha256": "56d4e6b8b461d45791ce1d4cc2185109554ecc57cc3163e68ad43c3001e91fbe" + }, + { + "path": "src/main/daemon/daemon-terminal-admission.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "14d1d8bbf4b662c98db123719741daffc3d0fe911d3a6a08d3516be666068251" + }, + { + "path": "src/main/daemon/pty-subprocess/subprocess-handle.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "12ec9e8b5e5f7175310f9522c3f827b19b871c3df5b09e8b1b2c4fe1fc92ba4e", + "matchesAuditedBefore": false + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "4623b60a0e362bb3cf218787573966aa056ee5fd1bdefc39fb5293446e4af70b", + "matchesAuditedBefore": false + } + }, + "auditedBeforeSha256": "e28a2043f9c68c365109aab0550f97c49ee76fbbadc14c81dd7eb7b9aae6fc7a" + }, + { + "path": "src/main/daemon/session.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "fcf79c354e29bf73549b3fdd4d905d431c210699391f45916bd27dbe858f86b4", + "matchesAuditedBefore": false + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "fcf79c354e29bf73549b3fdd4d905d431c210699391f45916bd27dbe858f86b4", + "matchesAuditedBefore": false + } + }, + "auditedBeforeSha256": "ddd71285f6e72bcee012e7a9955170be4fb59c8e41e70971bc629c13504a4338" + }, + { + "path": "src/main/runtime/orca-runtime-apply-tracked-pty-title.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "3c2ba07ff330875c5a9a07e44c0ffbcb1ebee1ae4af1952502ec733d6c7f7bea", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "afee7baf9568d05298c7a3b7b25057130f6ef21555f2e9079f6fa0bcef8f0084", + "matchesAuditedBefore": false + } + }, + "auditedBeforeSha256": "3c2ba07ff330875c5a9a07e44c0ffbcb1ebee1ae4af1952502ec733d6c7f7bea" + }, + { + "path": "src/main/runtime/orca-runtime-get-unpersisted-tracked-title-for-pty.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "107666bcc7e4bc744db51e9a023d659425947b134b9f420a0f11fb9ff0a7ba33", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "107666bcc7e4bc744db51e9a023d659425947b134b9f420a0f11fb9ff0a7ba33", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "107666bcc7e4bc744db51e9a023d659425947b134b9f420a0f11fb9ff0a7ba33" + }, + { + "path": "src/shared/agent-detection.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "b1d519786b07515e6518f8ab9e8bf826fb6879ac4d8866fb54f59918d3098651" + }, + { + "path": "src/shared/agent-name-token-match.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "27062a92d08e38aa140496d026e3e355cf5ad19f6ccdc18f1c06f4bbb9fef6f8" + }, + { + "path": "src/shared/agent-title-core.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "9827730762f0118274d98defd86ff8989238bb5fd4e0ef8012003ea00468301d" + }, + { + "path": "src/shared/agent-title-decoration.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "85cbebb6ca78a7d0a93f247f85da1efbce5584d5ade9154acb4a2b168c782b41" + }, + { + "path": "src/shared/agent-title-evidence.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "792a16e01191e3659e487352b21e6df8898db6cd2ab57c1363f3ec3ea1fd49fa", + "matchesAuditedBefore": false + } + }, + "auditedBeforeSha256": "5ea226cea3867f5421cd45ac54d37f57fc2d1364cb0eafa4a5ff71ddda5fc7ea" + }, + { + "path": "src/shared/agent-title-identity.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "c7876bbf40e0e14676f9829e9b9800baa527fe9f5d63ea7f721e293405ea18f9", + "matchesAuditedBefore": false + } + }, + "auditedBeforeSha256": "613c6284eb31aaa381db438c47d2ccf2c726ad150ae62ef269a2cfb762a03453" + }, + { + "path": "src/shared/agent-title-status.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "8df0706f4074d06909d1264e22203f431a09118e056e934b96758d963a69f1bd", + "matchesAuditedBefore": false + } + }, + "auditedBeforeSha256": "570a4710ebd0a7626dbdccff795efd087997853ac7c0fdb82c015976760b70bb" + }, + { + "path": "src/shared/github/links.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "3a7bdfabed6fa37272df8c60c6933bf1bb3b783d7ca4adddf9c8d4c579295da7", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "3a7bdfabed6fa37272df8c60c6933bf1bb3b783d7ca4adddf9c8d4c579295da7", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "3a7bdfabed6fa37272df8c60c6933bf1bb3b783d7ca4adddf9c8d4c579295da7" + }, + { + "path": "src/shared/opencode-terminal-title.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "2af51932ca77ae4d4b6edcc1d25ab181b3fdf392056f75c9defb6a14a93cc5ec" + }, + { + "path": "src/shared/osc-title-extraction.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "862f2e9f5d05e3b588143e18752dbffe9b7635174abc31893ba0da4f04c9230a" + }, + { + "path": "src/shared/own-retained-string.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": null, + "matchesAuditedBefore": false + } + }, + "auditedBeforeSha256": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2" + }, + { + "path": "src/shared/owned-utf16-suffix.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + { + "path": "src/shared/pane-agent-evidence-sources.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "26673ce2168f819d3c6c41a0e05544457f0c65ac4d70eb4b5e791bb05b239a8f" + }, + { + "path": "src/shared/pane-agent-identity-adapter.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "0d27d248d6cd86eaca4d8f046cbfeae0ee1934fc67bd2cb0bb9d7e8e630cdfb9" + }, + { + "path": "src/shared/pi-compatible-synthetic-title.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "2c43b6aa0b26f328bc7d51bfc8b4f7a8937156f91b430ccedc945827808e188d", + "matchesAuditedBefore": false + } + }, + "auditedBeforeSha256": "99b401985234b242229f9f7eb244b041ffe98f0e463cf90a74c1c248943b820f" + }, + { + "path": "src/shared/pi-state-title-marker.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "5cccf1bb0e00d362e9a824996a6755cf101c71a895413e6862c7a3d68e8828af", + "matchesAuditedBefore": false + } + }, + "auditedBeforeSha256": "c398ce62a0f0dc72b23d106742833c1d60807f4dee92c78adb428a0b0031dce8" + }, + { + "path": "src/shared/shell-process-detection.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "d80a58eaba385d17ef3af37a8b7c516c86539c494aa38afaef1418b1a2f4f79f" + }, + { + "path": "src/shared/synthetic-agent-title.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "8944067df16920a6ed068a251d6cf4c270263db68e9b489708d0ae467ae9326c", + "matchesAuditedBefore": false + } + }, + "auditedBeforeSha256": "40658e43e59cba687ab2304989d23ae14ed7205c5cae02f6fc7c9634bd0da922" + }, + { + "path": "src/shared/terminal-bell-detector.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "fffa4e0717222ea3059b4aab06a3403b6c972398d5fa9e4acf3316159d18dc05", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "fffa4e0717222ea3059b4aab06a3403b6c972398d5fa9e4acf3316159d18dc05", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "fffa4e0717222ea3059b4aab06a3403b6c972398d5fa9e4acf3316159d18dc05" + }, + { + "path": "src/shared/terminal-color-scheme-protocol.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "b8d1ee7693ae5053a8e3c9c099b8ab81032aed1f9a984de6eaad59e8c10c1700", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "b8d1ee7693ae5053a8e3c9c099b8ab81032aed1f9a984de6eaad59e8c10c1700", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "b8d1ee7693ae5053a8e3c9c099b8ab81032aed1f9a984de6eaad59e8c10c1700" + }, + { + "path": "src/shared/terminal-github-pr-link-detector.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "7c9509c50cfe733b5f9b29bcbd5c684825b3c18b2fac82275bd9047e7026100e", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "7c9509c50cfe733b5f9b29bcbd5c684825b3c18b2fac82275bd9047e7026100e", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "7c9509c50cfe733b5f9b29bcbd5c684825b3c18b2fac82275bd9047e7026100e" + }, + { + "path": "src/shared/terminal-mode-2031-final-state.test.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "42f3ff8839969f36a98f05c36d93abb7da291a9c3d2923a185a4933f4f245e10", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "42f3ff8839969f36a98f05c36d93abb7da291a9c3d2923a185a4933f4f245e10", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "42f3ff8839969f36a98f05c36d93abb7da291a9c3d2923a185a4933f4f245e10" + }, + { + "path": "src/shared/terminal-osc133-command-finished.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "1790083b5200d11e68257556dffb49d2e8700841dccc6ccbbaa2c30b11b1efcd", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "1790083b5200d11e68257556dffb49d2e8700841dccc6ccbbaa2c30b11b1efcd", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "1790083b5200d11e68257556dffb49d2e8700841dccc6ccbbaa2c30b11b1efcd" + }, + { + "path": "src/shared/terminal-output-side-effects.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "f609970e2ea7e53fc9e1ab0e0caca038b910dee3c092a9297377689871845f49", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "c1666a54339ece63e4180ab7e3eceec244f5bdd686c6dc3a0eed6e9ab93abcd4", + "matchesAuditedBefore": false + } + }, + "auditedBeforeSha256": "f609970e2ea7e53fc9e1ab0e0caca038b910dee3c092a9297377689871845f49" + }, + { + "path": "src/shared/terminal-title-agent-type.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "d7734bdfbd4d3151ae5bef3545f670c5ea931f1b17758cbbab8476b83b448864" + }, + { + "path": "src/shared/terminal-title-classification-memo.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "e587a8cbfd3cf6d5fdf95400020a9346f87518e8b9d8476039d6a88a7b99beda" + }, + { + "path": "src/shared/terminal-title-wrapper-segments.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "f2eb6fc485e519263f3e9394a9f3dc7004aa70373e3e36d42eba3ec509e2cb78" + }, + { + "path": "src/shared/tui-agent-display-names.ts", + "namedRefs": { + "main291b": { + "commit": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sha256": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "matchesAuditedBefore": true + }, + "v1.4.198": { + "commit": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sha256": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296", + "matchesAuditedBefore": true + } + }, + "auditedBeforeSha256": "1a4b102bb7e8c13a48344c6da22f531e1ebf77329bd868f2580b7729684c7296" + } + ], + "historicalScope": "Scanner baseline is identical at main291b and v1.4.198. Current caller/helper dependencies are evaluated; own-retained-string is absent in v1.4.198. This is not a complete historical-release replay.", + "callerScope": "Non-evaluated supporting caller provenance accepts only recorded audited-before or named main291b bytes; each runtime report records the actual selected hash. Evaluated bundle dependencies require the single fixed hash." +} diff --git a/docs/audits/osc133-carry-retention/sources.cjs b/docs/audits/osc133-carry-retention/sources.cjs new file mode 100644 index 00000000000..6bfa507dfce --- /dev/null +++ b/docs/audits/osc133-carry-retention/sources.cjs @@ -0,0 +1,87 @@ +const assert = require('node:assert/strict') +const { createHash } = require('node:crypto') +const { readFileSync } = require('node:fs') +const path = require('node:path') +const Module = require('node:module') +const { build } = require('esbuild') +const { applyPatch, parsePatch, reversePatch } = require('diff') + +const root = path.resolve(__dirname, '../../..') +const canonical = (value) => value.replaceAll('\r\n', '\n') +const sha = (value) => createHash('sha256').update(value).digest('hex') +const readText = (file) => canonical(readFileSync(file, 'utf8')) +const versions = JSON.parse(readText(path.join(__dirname, 'source-versions.json'))) + +function loadSources(read = readText) { + const fixed = canonical(read(path.join(root, versions.sourcePath))) + assert.equal(sha(fixed), versions.fixedSha256, 'Fixed scanner drift') + const patches = parsePatch(canonical(read(path.join(__dirname, 'fix.patch')))) + assert.equal(patches.length, 1) + assert.equal(patches[0].newFileName, `b/${versions.sourcePath}`) + const baseline = applyPatch(fixed, reversePatch(patches[0])) + assert.notEqual(baseline, false) + assert.equal(sha(baseline), versions.baselineSha256, 'Baseline scanner drift') + return { baseline, fixed } +} + +async function load(fixed) { + const sources = loadSources() + const callerSourceHashes = {} + for (const caller of versions.callerHashes) { + const actual = sha(readText(path.join(root, caller.path))) + assert.ok(caller.acceptedSha256.includes(actual), `Caller drift: ${caller.path}`) + callerSourceHashes[caller.path] = actual + } + const evaluatedSources = {} + const built = await build({ + stdin: { + contents: [ + "export { createOsc133CommandFinishedScanner } from './src/shared/terminal-osc133-command-finished'", + "export { BackgroundTransientFactRelay } from './src/main/daemon/daemon-background-transient-facts'", + "export { createTerminalTitleTracker } from './src/shared/terminal-output-side-effects'", + "export { ownRetainedString, resetOwnRetainedStringCopier } from './src/shared/own-retained-string'" + ].join('\n'), + resolveDir: root, + loader: 'ts' + }, + absWorkingDir: root, + bundle: true, + platform: 'node', + format: 'cjs', + write: false, + plugins: [ + { + name: 'hash-fenced-osc133-carry', + setup(builder) { + builder.onLoad({ filter: /\.ts$/ }, ({ path: filename }) => { + const relative = path.relative(root, filename).split(path.sep).join('/') + const expected = versions.dependencies[relative] + assert.ok(expected, `Unreviewed dependency: ${relative}`) + let contents = readText(filename) + assert.equal(sha(contents), expected, `Dependency drift: ${relative}`) + if (relative === versions.sourcePath) { + contents = fixed ? sources.fixed : sources.baseline + } + evaluatedSources[relative] = sha(contents) + return { contents, loader: 'ts' } + }) + } + } + ] + }) + assert.deepEqual(Object.keys(evaluatedSources).sort(), Object.keys(versions.dependencies).sort()) + const filename = path.join(__dirname, fixed ? 'fixed-bundle.cjs' : 'baseline-bundle.cjs') + const loaded = new Module(filename, module) + loaded.filename = filename + loaded.paths = Module._nodeModulePaths(root) + loaded._compile(built.outputFiles[0].text, filename) + return { + api: loaded.exports, + sourceSha256: sha(fixed ? sources.fixed : sources.baseline), + bundleSha256: sha(built.outputFiles[0].text), + evaluatedSources, + callerSourceHashes + } +} + +module.exports = { load, loadSources, readText, root, sha, versions } diff --git a/docs/audits/osc133-carry-retention/validation.json b/docs/audits/osc133-carry-retention/validation.json new file mode 100644 index 00000000000..50d981a6b57 --- /dev/null +++ b/docs/audits/osc133-carry-retention/validation.json @@ -0,0 +1,83 @@ +{ + "backgroundLaunch": "All tests and proofs used ORCA_BACKGROUND_LAUNCH=1; Electron only ran with ELECTRON_RUN_AS_NODE=1. No app or native PTY.", + "fixedTests": { + "passed": 45, + "failed": 0, + "files": 4, + "newTests": 8, + "config": "config/vitest.config.ts" + }, + "baselineOverlay": { + "passed": 41, + "failed": 4, + "config": "docs/audits/osc133-carry-retention/before.config.mjs", + "intendedFailures": [ + { + "test": "OSC 133 carry with Bufferless copying=false owns a retained fish suffix \"\\u001b]133;A;click_events=1\"", + "assertion": "AssertionError: expected 33560392 to be less than 2097152", + "retainedBytes": 33560392 + }, + { + "test": "OSC 133 carry with Bufferless copying=false owns a retained fish suffix \"\\u001b]133;C;cmdline_url=npx\"", + "assertion": "AssertionError: expected 33565360 to be less than 2097152", + "retainedBytes": 33565360 + }, + { + "test": "OSC 133 carry with Bufferless copying=true owns a retained fish suffix \"\\u001b]133;A;click_events=1\"", + "assertion": "AssertionError: expected 33557160 to be less than 2097152", + "retainedBytes": 33557160 + }, + { + "test": "OSC 133 carry with Bufferless copying=true owns a retained fish suffix \"\\u001b]133;C;cmdline_url=npx\"", + "assertion": "AssertionError: expected 33550680 to be less than 2097152", + "retainedBytes": 33550680 + } + ] + }, + "portableProofs": { + "nodeCases": 117, + "electronCases": 117, + "crlfSourceAndPatchReads": 2, + "evaluatedModules": 28, + "nonEvaluatedCallerFiles": 7, + "variants": ["baseline", "fixed Buffer copier", "fixed Bufferless copier"] + }, + "typechecks": { + "node": "Passed full Node project; parent root rerun after resolving its concurrent viewport-test typing.", + "web": "Passed full Web project in parent root shared desktop run.", + "cli": "Passed full CLI project in parent root shared desktop run." + }, + "fullPublicationQuality": { + "paths": [ + "src/shared/terminal-osc133-command-finished.ts", + "src/shared/terminal-osc133-carry-retention.test.ts", + "docs/audits/osc133-carry-retention/sources.cjs", + "docs/audits/osc133-carry-retention/scenario.cjs", + "docs/audits/osc133-carry-retention/reproduce.cjs", + "docs/audits/osc133-carry-retention/before.config.mjs" + ], + "scans": [ + "default rules and unused suppression", + "casting", + "type-aware", + "React Doctor", + "design system" + ], + "result": "All five full-file scans passed with --deny-warnings, including CJS/MJS artifact files." + }, + "changedQuality": { + "base": "4a09b1d108cfd8b57ffcc5d727b3a3bd71ae71fb", + "result": "Passed all five scans plus SAFETY rationale gate across six concurrent changed files; artifacts separately covered by explicit full-file scans." + }, + "productHashes": [ + { + "path": "src/shared/terminal-osc133-command-finished.ts", + "sha256": "92bc1aa5eb975672c3b8761cce0da37d7ff6263c8f38c69d0715f73bd8e075d0" + }, + { + "path": "src/shared/terminal-osc133-carry-retention.test.ts", + "sha256": "f123ddacc2d395f5896dc91acba13fbd9447f9152d36b8164a11d83271d9279d" + } + ], + "limits": "Synthetic parent size/boundary with captured fish sequence syntax. Heap deltas are not RSS. No historical whole-app or incident attribution. Baseline overlay retains current dependency implementations." +} diff --git a/docs/audits/plugin-worker-output-retention/README.md b/docs/audits/plugin-worker-output-retention/README.md new file mode 100644 index 00000000000..8ce1784c854 --- /dev/null +++ b/docs/audits/plugin-worker-output-retention/README.md @@ -0,0 +1,67 @@ +# Plugin worker output retention + +The worker output parser capped a line at 8,192 code units, but retained slices could keep a much larger decoded input chunk alive. This artifact reproduces two ownership paths using the actual parser and actual `PluginLogBuffer`: + +1. An unfinished line stays in the stream listener's buffer. +2. A completed short or truncated line stays in the service's 200-entry log ring. + +The fix uses the existing `ownRetainedString` copier for incomplete segments retained across callbacks and for the bounded string passed to the log sink. Line contents, truncation, callback invocation, ring capacity, and worker lifecycle are unchanged. Strings shorter than 13 code units keep the helper's existing fast path. + +## Production reachability and lifetime + +- `src/main/plugins/plugin-host-process.ts` installs the parser on child stdout and stderr at lines 101–102, with UTF-8 decoding in the parser. The production sink passes through `plugin-worker-manager.ts:148` and `plugin-service.ts:94` to `plugin-log-buffer.ts:14`, which stores the original string without copying it. +- The parser retains at most one incomplete line per stream. The default five active workers allow ten live stdout/stderr buffers. Worker slots are acquired before startup. Idle workers are reaped after five minutes, checked every minute; stream end clears parser buffering. +- The log ring belongs to the long-lived `PluginService`, not the worker. Worker exit and stream end preserve its last 200 entries per plugin. Ring eviction releases the entries. Several lines can share one parent; the backing allocation must be counted once. +- This is a main-process plugin path. The plugin-system setting gates activation (`src/main/startup/main-process-plugins.ts:59–62`). It is not a terminal daemon or renderer retention path. The plugin `orca.log` IPC message is a separate producer. +- `PluginService.getLogs` and its IPC handler expose the existing ring. Reading or serializing a concatenated string can flatten it and shorten its parent retention, but does not remove the service's ring entries. + +## Reproduce + +From the repository root with the project's dependencies installed: + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/plugin-worker-output-retention/reproduce.cjs +``` + +For Electron, run the installed Electron executable with `ELECTRON_RUN_AS_NODE=1`, `ORCA_BACKGROUND_LAUNCH=1`, and the same arguments. This uses Node mode without opening an app window. For example, on macOS: + +```sh +ORCA_BACKGROUND_LAUNCH=1 ELECTRON_RUN_AS_NODE=1 node_modules/electron/dist/Electron.app/Contents/MacOS/Electron --expose-gc --max-old-space-size=192 docs/audits/plugin-worker-output-retention/reproduce.cjs +``` + +The runner writes `node-results.json` or `electron-results.json` beside itself. Pass `--output ` to preserve the captured reports. It uses inert PassThrough streams, no OS child process or network, a 192 MiB heap limit, and a 30-second deadline. + +`sources.cjs` reverses `fix.patch` in memory and checks exact baseline/fixed parser hashes. It also checks eight dependency/caller hashes and records actual evaluated source and bundle hashes. No source files are overwritten. A synthetic CRLF read control checks all ten source/patch reads. + +`source-versions.json` records identical parser, sink, caller, and helper hashes at main checkpoint `291b4ddd6f1c1af480169885e0fda7f9c78ff053`, main `f78483ec29891ab11f49bb25e6cd628837b1242e`, and the #20960 topic `np-oom-scan-retained-text-slices` at `0d2efbc0d3b4f902b9bc02f5451c6ff4291405cf`. The parser, sink and caller modules also match v1.4.198 (`e0826956fcfc532f5a1e55b5e081f2e57e553c43`), which lacks the newer `own-retained-string.ts` wrapper. The baseline bundle uses only the unchanged parser and ring; fixed variants use the recorded publication helper. These are source controls with current build dependencies, not a historical app binary. + +## Controls and results + +Both Node 26.6 and Electron 43.7 / Node 24.21 pass 24 cases: baseline, diagnostic tail-only copy, fixed Buffer copy, and fixed code-unit-copy fallback, each with two input sizes and three ownership cases. The fallback is a shared-helper compatibility control; production main normally has Buffer. + +| Retained owner | Baseline heap delta | Fixed heap delta | +| ----------------------------------------------- | ------------------: | ---------------: | +| Ten unfinished tails, 64 KiB input each | 0.72–0.73 MB | 9–24 KB | +| Eight unfinished tails, 4 MiB input each | 33.56–33.57 MB | 6–11 KB | +| 200 short log rows from 205 × 64 KiB inputs | 13.14–13.16 MB | 27–45 KB | +| 200 truncated log rows from 205 × 64 KiB inputs | 13.14–13.15 MB | 3.29–3.31 MB | +| Eight short log rows, 4 MiB input each | 33.56 MB | about 1 KB | +| Eight truncated log rows, 4 MiB input each | 33.56 MB | 128–132 KB | + +Heap deltas include GC noise. Truncated strings legitimately retain 8,192 code units, including the non-ASCII truncation suffix. Tail-only copying fixes unfinished buffers but leaves both log-ring paths. Stream end clears no-op-sink tails while the actual ring remains live; replacing all 200 entries releases the original parents. + +64 KiB is an ordinary-scale stdio input control. The 4 MiB input is amplified stress, not a claim about normal OS pipe reads. PassThrough delivers the selected chunk intact; real child-pipe chunk sizes depend on runtime and OS. Retention is bounded by owner count, ring capacity and backing input size; this is not an unbounded line queue. + +Behavior comparisons cover blank and split lines, null/empty streams, CRLF, end flushing, discard/resume after overflow, log level, exact ring content, NUL, lone surrogates, emoji, and the code-unit limit. Value comparisons run separately from heap controls because comparing concatenated strings can flatten them and change retention. + +## Validation + +```sh +ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/plugins/plugin-worker-output-retention.test.ts src/main/plugins/plugin-worker-output-buffer.test.ts src/main/plugins/plugin-host-process.test.ts src/shared/own-retained-string.test.ts +ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config docs/audits/plugin-worker-output-retention/before.config.mjs src/main/plugins/plugin-worker-output-retention.test.ts src/main/plugins/plugin-worker-output-buffer.test.ts +ORCA_BACKGROUND_LAUNCH=1 pnpm tc:node +``` + +The fixed source passes 20 tests. The baseline overlay intentionally fails all three new heap regressions: approximately 33.6 MB for unfinished tails and 13.2 MB for each ring case, against 2 MiB and 5 MiB ceilings; its original behavior test passes. Node typecheck passes. All five changed-quality scan configurations pass over all five product/test/artifact code files with `--no-ignore --deny-warnings`, including the ordinary and type-aware lint rules. + +This proves a reachable code mechanism and its repair. It does not establish affected-host plugin use, output cadence, aggregate app RSS, or attribution to #19831 or another incident. diff --git a/docs/audits/plugin-worker-output-retention/before.config.mjs b/docs/audits/plugin-worker-output-retention/before.config.mjs new file mode 100644 index 00000000000..607bce497cf --- /dev/null +++ b/docs/audits/plugin-worker-output-retention/before.config.mjs @@ -0,0 +1,23 @@ +import { resolve } from 'node:path' +import { createRequire } from 'node:module' +import { defineConfig, mergeConfig } from 'vitest/config' +import baseConfig from '../../../config/vitest.config.ts' + +const { loadSources } = createRequire(import.meta.url)('./sources.cjs') +const { before } = loadSources() +const sourcePath = resolve('src/main/plugins/plugin-worker-output-buffer.ts') + +export default mergeConfig( + baseConfig, + defineConfig({ + plugins: [ + { + name: 'plugin-output-before-fix', + enforce: 'pre', + transform(_code, id) { + return resolve(id.split('?')[0]) === sourcePath ? { code: before, map: null } : undefined + } + } + ] + }) +) diff --git a/docs/audits/plugin-worker-output-retention/electron-results.json b/docs/audits/plugin-worker-output-retention/electron-results.json new file mode 100644 index 00000000000..2a484e31dd2 --- /dev/null +++ b/docs/audits/plugin-worker-output-retention/electron-results.json @@ -0,0 +1,493 @@ +{ + "runtime": { + "node": "24.21.0", + "acorn": "8.18.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.2", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "148", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "0.0.0", + "simdjson": "4.6.7", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2025c", + "undici": "7.29.1", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "15.0.245.31-electron.0", + "zlib": "1.3.2.1-motley", + "zstd": "1.6.0", + "electron": "43.7.0", + "chrome": "150.0.7871.250" + }, + "artifactHashes": { + "reproduce.cjs": "1fe840d9b4ecc76c42cc2e8bcb87c54db78a91510819f755403a01ecb49181da", + "sources.cjs": "8390b117b07ff8c0638e02183624d8da71b32cea955a366e16c80701dd9c38b1", + "source-versions.json": "247bf1f8587b37129212582e88b792dc345197666b6ceadaddc12eaa65f84de7", + "fix.patch": "ec28362681138dbc311e1c1a14154c250f418423bbb3d30cc75adfc46bbb5d57" + }, + "crlfLoaderControl": { + "reads": 10, + "identical": true + }, + "bundles": { + "before": { + "checkedSources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "evaluatedSources": { + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-worker-output-buffer.ts": "969daeddb4f4fde9d53e80b008438aa2942526af940ae9eb4f08cd9440a0f914" + }, + "sourceVersionsSha256": "247bf1f8587b37129212582e88b792dc345197666b6ceadaddc12eaa65f84de7", + "bundleSha256": "c44c04feb9f55a1fb05da64a8bd34c4bbb92d292d3f070062705d8b09dfc34a0" + }, + "tail-only": { + "checkedSources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/main/plugins/plugin-worker-output-buffer.ts": "5d68af2f66e1466b5a642fa9bb301b8f04ccd2c2741dd5731faac7a2f30c5be5", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "sourceVersionsSha256": "247bf1f8587b37129212582e88b792dc345197666b6ceadaddc12eaa65f84de7", + "bundleSha256": "12a2b78410879874c0f6f36af3f6463a68d38c93ce4a098a87bffc24f5ac98b0" + }, + "fixed-buffer": { + "checkedSources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-worker-output-buffer.ts": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "sourceVersionsSha256": "247bf1f8587b37129212582e88b792dc345197666b6ceadaddc12eaa65f84de7", + "bundleSha256": "c1b3988c001cf1545164a1cab25cf152af5eb366adae5ae11deb1a5b469a2162" + }, + "fixed-fallback": { + "checkedSources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/main/plugins/plugin-worker-output-buffer.ts": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "sourceVersionsSha256": "247bf1f8587b37129212582e88b792dc345197666b6ceadaddc12eaa65f84de7", + "bundleSha256": "c1b3988c001cf1545164a1cab25cf152af5eb366adae5ae11deb1a5b469a2162" + } + }, + "behaviors": { + "before": { + "lines": [ + ["error", "hello world"], + [ + "error", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx… [truncated]" + ], + ["error", "ok"], + ["error", "retained-output-tail"] + ], + "unicode": [ + ["info", "short"], + ["info", "twelve chars"], + ["info", "\ud800a\udfff\u0000漢"], + [ + "info", + "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀" + ], + [ + "info", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… [truncated]" + ], + [ + "info", + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq… [truncated]" + ], + ["info", "next\r"], + ["info", "unterminated 😀"] + ] + }, + "tail-only": { + "lines": [ + ["error", "hello world"], + [ + "error", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx… [truncated]" + ], + ["error", "ok"], + ["error", "retained-output-tail"] + ], + "unicode": [ + ["info", "short"], + ["info", "twelve chars"], + ["info", "\ud800a\udfff\u0000漢"], + [ + "info", + "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀" + ], + [ + "info", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… [truncated]" + ], + [ + "info", + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq… [truncated]" + ], + ["info", "next\r"], + ["info", "unterminated 😀"] + ] + }, + "fixed-buffer": { + "lines": [ + ["error", "hello world"], + [ + "error", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx… [truncated]" + ], + ["error", "ok"], + ["error", "retained-output-tail"] + ], + "unicode": [ + ["info", "short"], + ["info", "twelve chars"], + ["info", "\ud800a\udfff\u0000漢"], + [ + "info", + "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀" + ], + [ + "info", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… [truncated]" + ], + [ + "info", + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq… [truncated]" + ], + ["info", "next\r"], + ["info", "unterminated 😀"] + ] + }, + "fixed-fallback": { + "lines": [ + ["error", "hello world"], + [ + "error", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx… [truncated]" + ], + ["error", "ok"], + ["error", "retained-output-tail"] + ], + "unicode": [ + ["info", "short"], + ["info", "twelve chars"], + ["info", "\ud800a\udfff\u0000漢"], + [ + "info", + "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀" + ], + [ + "info", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… [truncated]" + ], + [ + "info", + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq… [truncated]" + ], + ["info", "next\r"], + ["info", "unterminated 😀"] + ] + } + }, + "reports": [ + { + "kind": "tail", + "variant": "before", + "chars": 65536, + "count": 10, + "heldDelta": 716564, + "endedDelta": 122728 + }, + { + "kind": "tail", + "variant": "before", + "chars": 4194304, + "count": 8, + "heldDelta": 33561956, + "endedDelta": 8432 + }, + { + "kind": "ring", + "variant": "before", + "chars": 65536, + "count": 205, + "truncated": false, + "expectedParents": 200, + "heldDelta": 13141604, + "endedDelta": 13141904, + "evictedDelta": 28332 + }, + { + "kind": "ring", + "variant": "before", + "chars": 4194304, + "count": 8, + "truncated": false, + "expectedParents": 8, + "heldDelta": 33555548, + "endedDelta": 33556112, + "evictedDelta": 9188 + }, + { + "kind": "ring", + "variant": "before", + "chars": 65536, + "count": 205, + "truncated": true, + "expectedParents": 200, + "heldDelta": 13135672, + "endedDelta": 13135728, + "evictedDelta": 21692 + }, + { + "kind": "ring", + "variant": "before", + "chars": 4194304, + "count": 8, + "truncated": true, + "expectedParents": 8, + "heldDelta": 33555300, + "endedDelta": 33555356, + "evictedDelta": 5060 + }, + { + "kind": "tail", + "variant": "tail-only", + "chars": 65536, + "count": 10, + "heldDelta": 7664, + "endedDelta": 8548 + }, + { + "kind": "tail", + "variant": "tail-only", + "chars": 4194304, + "count": 8, + "heldDelta": 6068, + "endedDelta": 6924 + }, + { + "kind": "ring", + "variant": "tail-only", + "chars": 65536, + "count": 205, + "truncated": false, + "expectedParents": 200, + "heldDelta": 13137012, + "endedDelta": 13137068, + "evictedDelta": 23480 + }, + { + "kind": "ring", + "variant": "tail-only", + "chars": 4194304, + "count": 8, + "truncated": false, + "expectedParents": 8, + "heldDelta": 33555144, + "endedDelta": 33555200, + "evictedDelta": 8276 + }, + { + "kind": "ring", + "variant": "tail-only", + "chars": 65536, + "count": 205, + "truncated": true, + "expectedParents": 200, + "heldDelta": 13128492, + "endedDelta": 13128548, + "evictedDelta": 19872 + }, + { + "kind": "ring", + "variant": "tail-only", + "chars": 4194304, + "count": 8, + "truncated": true, + "expectedParents": 8, + "heldDelta": 33555236, + "endedDelta": 33555292, + "evictedDelta": 8208 + }, + { + "kind": "tail", + "variant": "fixed-buffer", + "chars": 65536, + "count": 10, + "heldDelta": 8944, + "endedDelta": 14304 + }, + { + "kind": "tail", + "variant": "fixed-buffer", + "chars": 4194304, + "count": 8, + "heldDelta": 10076, + "endedDelta": 9740 + }, + { + "kind": "ring", + "variant": "fixed-buffer", + "chars": 65536, + "count": 205, + "truncated": false, + "expectedParents": 200, + "heldDelta": 26624, + "endedDelta": 26680, + "evictedDelta": 18692 + }, + { + "kind": "ring", + "variant": "fixed-buffer", + "chars": 4194304, + "count": 8, + "truncated": false, + "expectedParents": 8, + "heldDelta": 776, + "endedDelta": 832, + "evictedDelta": 8276 + }, + { + "kind": "ring", + "variant": "fixed-buffer", + "chars": 65536, + "count": 205, + "truncated": true, + "expectedParents": 200, + "heldDelta": 3296796, + "endedDelta": 3296852, + "evictedDelta": 20952 + }, + { + "kind": "ring", + "variant": "fixed-buffer", + "chars": 4194304, + "count": 8, + "truncated": true, + "expectedParents": 8, + "heldDelta": 131556, + "endedDelta": 131612, + "evictedDelta": 8208 + }, + { + "kind": "tail", + "variant": "fixed-fallback", + "chars": 65536, + "count": 10, + "heldDelta": 8944, + "endedDelta": 8596 + }, + { + "kind": "tail", + "variant": "fixed-fallback", + "chars": 4194304, + "count": 8, + "heldDelta": 6068, + "endedDelta": 10048 + }, + { + "kind": "ring", + "variant": "fixed-fallback", + "chars": 65536, + "count": 205, + "truncated": false, + "expectedParents": 200, + "heldDelta": 33472, + "endedDelta": 39568, + "evictedDelta": 31580 + }, + { + "kind": "ring", + "variant": "fixed-fallback", + "chars": 4194304, + "count": 8, + "truncated": false, + "expectedParents": 8, + "heldDelta": 776, + "endedDelta": 832, + "evictedDelta": 8276 + }, + { + "kind": "ring", + "variant": "fixed-fallback", + "chars": 65536, + "count": 205, + "truncated": true, + "expectedParents": 200, + "heldDelta": 3290604, + "endedDelta": 3290660, + "evictedDelta": 14776 + }, + { + "kind": "ring", + "variant": "fixed-fallback", + "chars": 4194304, + "count": 8, + "truncated": true, + "expectedParents": 8, + "heldDelta": 131556, + "endedDelta": 131612, + "evictedDelta": 8208 + } + ] +} diff --git a/docs/audits/plugin-worker-output-retention/fix.patch b/docs/audits/plugin-worker-output-retention/fix.patch new file mode 100644 index 00000000000..06b54c9b1ca --- /dev/null +++ b/docs/audits/plugin-worker-output-retention/fix.patch @@ -0,0 +1,18 @@ +diff --git a/src/main/plugins/plugin-worker-output-buffer.ts b/src/main/plugins/plugin-worker-output-buffer.ts +index 836330c879..6a0cb2a078 100644 +--- a/src/main/plugins/plugin-worker-output-buffer.ts ++++ b/src/main/plugins/plugin-worker-output-buffer.ts +@@ -1,0 +2 @@ import type { Readable } from 'node:stream' ++import { ownRetainedString } from '../../shared/own-retained-string' +@@ -24,3 +25,5 @@ export function pipePluginWorkerOutput( +- truncated +- ? `${line.slice(0, PLUGIN_WORKER_OUTPUT_LINE_LIMIT - TRUNCATION_SUFFIX.length)}${TRUNCATION_SUFFIX}` +- : line ++ ownRetainedString( ++ truncated ++ ? `${line.slice(0, PLUGIN_WORKER_OUTPUT_LINE_LIMIT - TRUNCATION_SUFFIX.length)}${TRUNCATION_SUFFIX}` ++ : line ++ ) +@@ -52 +55 @@ export function pipePluginWorkerOutput( +- buffered += segment ++ buffered += newline === -1 ? ownRetainedString(segment) : segment diff --git a/docs/audits/plugin-worker-output-retention/node-results.json b/docs/audits/plugin-worker-output-retention/node-results.json new file mode 100644 index 00000000000..0f4eb1a4d4b --- /dev/null +++ b/docs/audits/plugin-worker-output-retention/node-results.json @@ -0,0 +1,492 @@ +{ + "runtime": { + "node": "26.6.0", + "acorn": "8.17.0", + "ada": "4.0.0", + "amaro": "1.1.11", + "ares": "1.34.8", + "brotli": "1.2.0", + "cldr": "48.0", + "icu": "78.3", + "libffi": "3.7.1", + "llhttp": "9.4.3", + "merve": "1.2.2", + "modules": "147", + "napi": "10", + "nbytes": "0.1.4", + "ncrypto": "0.0.1", + "nghttp2": "1.70.0", + "nghttp3": "", + "ngtcp2": "", + "openssl": "3.6.3", + "simdjson": "4.6.6", + "simdutf": "7.7.0", + "sqlite": "3.53.4", + "tz": "2026a", + "undici": "8.9.0", + "unicode": "17.0", + "uv": "1.52.1", + "uvwasi": "0.0.23", + "v8": "14.6.202.34-node.26", + "zlib": "1.2.12", + "zstd": "1.5.7" + }, + "artifactHashes": { + "reproduce.cjs": "1fe840d9b4ecc76c42cc2e8bcb87c54db78a91510819f755403a01ecb49181da", + "sources.cjs": "8390b117b07ff8c0638e02183624d8da71b32cea955a366e16c80701dd9c38b1", + "source-versions.json": "247bf1f8587b37129212582e88b792dc345197666b6ceadaddc12eaa65f84de7", + "fix.patch": "ec28362681138dbc311e1c1a14154c250f418423bbb3d30cc75adfc46bbb5d57" + }, + "crlfLoaderControl": { + "reads": 10, + "identical": true + }, + "bundles": { + "before": { + "checkedSources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "evaluatedSources": { + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-worker-output-buffer.ts": "969daeddb4f4fde9d53e80b008438aa2942526af940ae9eb4f08cd9440a0f914" + }, + "sourceVersionsSha256": "247bf1f8587b37129212582e88b792dc345197666b6ceadaddc12eaa65f84de7", + "bundleSha256": "c44c04feb9f55a1fb05da64a8bd34c4bbb92d292d3f070062705d8b09dfc34a0" + }, + "tail-only": { + "checkedSources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/main/plugins/plugin-worker-output-buffer.ts": "5d68af2f66e1466b5a642fa9bb301b8f04ccd2c2741dd5731faac7a2f30c5be5", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "sourceVersionsSha256": "247bf1f8587b37129212582e88b792dc345197666b6ceadaddc12eaa65f84de7", + "bundleSha256": "12a2b78410879874c0f6f36af3f6463a68d38c93ce4a098a87bffc24f5ac98b0" + }, + "fixed-buffer": { + "checkedSources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "evaluatedSources": { + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/main/plugins/plugin-worker-output-buffer.ts": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "sourceVersionsSha256": "247bf1f8587b37129212582e88b792dc345197666b6ceadaddc12eaa65f84de7", + "bundleSha256": "c1b3988c001cf1545164a1cab25cf152af5eb366adae5ae11deb1a5b469a2162" + }, + "fixed-fallback": { + "checkedSources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/main/plugins/plugin-worker-output-buffer.ts": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "sourceVersionsSha256": "247bf1f8587b37129212582e88b792dc345197666b6ceadaddc12eaa65f84de7", + "bundleSha256": "c1b3988c001cf1545164a1cab25cf152af5eb366adae5ae11deb1a5b469a2162" + } + }, + "behaviors": { + "before": { + "lines": [ + ["error", "hello world"], + [ + "error", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx… [truncated]" + ], + ["error", "ok"], + ["error", "retained-output-tail"] + ], + "unicode": [ + ["info", "short"], + ["info", "twelve chars"], + ["info", "\ud800a\udfff\u0000漢"], + [ + "info", + "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀" + ], + [ + "info", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… [truncated]" + ], + [ + "info", + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq… [truncated]" + ], + ["info", "next\r"], + ["info", "unterminated 😀"] + ] + }, + "tail-only": { + "lines": [ + ["error", "hello world"], + [ + "error", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx… [truncated]" + ], + ["error", "ok"], + ["error", "retained-output-tail"] + ], + "unicode": [ + ["info", "short"], + ["info", "twelve chars"], + ["info", "\ud800a\udfff\u0000漢"], + [ + "info", + "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀" + ], + [ + "info", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… [truncated]" + ], + [ + "info", + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq… [truncated]" + ], + ["info", "next\r"], + ["info", "unterminated 😀"] + ] + }, + "fixed-buffer": { + "lines": [ + ["error", "hello world"], + [ + "error", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx… [truncated]" + ], + ["error", "ok"], + ["error", "retained-output-tail"] + ], + "unicode": [ + ["info", "short"], + ["info", "twelve chars"], + ["info", "\ud800a\udfff\u0000漢"], + [ + "info", + "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀" + ], + [ + "info", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… [truncated]" + ], + [ + "info", + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq… [truncated]" + ], + ["info", "next\r"], + ["info", "unterminated 😀"] + ] + }, + "fixed-fallback": { + "lines": [ + ["error", "hello world"], + [ + "error", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx… [truncated]" + ], + ["error", "ok"], + ["error", "retained-output-tail"] + ], + "unicode": [ + ["info", "short"], + ["info", "twelve chars"], + ["info", "\ud800a\udfff\u0000漢"], + [ + "info", + "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀" + ], + [ + "info", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… [truncated]" + ], + [ + "info", + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq… [truncated]" + ], + ["info", "next\r"], + ["info", "unterminated 😀"] + ] + } + }, + "reports": [ + { + "kind": "tail", + "variant": "before", + "chars": 65536, + "count": 10, + "heldDelta": 734088, + "endedDelta": 138584 + }, + { + "kind": "tail", + "variant": "before", + "chars": 4194304, + "count": 8, + "heldDelta": 33573744, + "endedDelta": 20328 + }, + { + "kind": "ring", + "variant": "before", + "chars": 65536, + "count": 205, + "truncated": false, + "expectedParents": 200, + "heldDelta": 13157504, + "endedDelta": 13157944, + "evictedDelta": 41424 + }, + { + "kind": "ring", + "variant": "before", + "chars": 4194304, + "count": 8, + "truncated": false, + "expectedParents": 8, + "heldDelta": 33556256, + "endedDelta": 33557296, + "evictedDelta": 16464 + }, + { + "kind": "ring", + "variant": "before", + "chars": 65536, + "count": 205, + "truncated": true, + "expectedParents": 200, + "heldDelta": 13150592, + "endedDelta": 13150688, + "evictedDelta": 31088 + }, + { + "kind": "ring", + "variant": "before", + "chars": 4194304, + "count": 8, + "truncated": true, + "expectedParents": 8, + "heldDelta": 33555760, + "endedDelta": 33555856, + "evictedDelta": 14768 + }, + { + "kind": "tail", + "variant": "tail-only", + "chars": 65536, + "count": 10, + "heldDelta": 22120, + "endedDelta": 29824 + }, + { + "kind": "tail", + "variant": "tail-only", + "chars": 4194304, + "count": 8, + "heldDelta": 11248, + "endedDelta": 12272 + }, + { + "kind": "ring", + "variant": "tail-only", + "chars": 65536, + "count": 205, + "truncated": false, + "expectedParents": 200, + "heldDelta": 13149032, + "endedDelta": 13149128, + "evictedDelta": 32344 + }, + { + "kind": "ring", + "variant": "tail-only", + "chars": 4194304, + "count": 8, + "truncated": false, + "expectedParents": 8, + "heldDelta": 32504472, + "endedDelta": 32504568, + "evictedDelta": -1036264 + }, + { + "kind": "ring", + "variant": "tail-only", + "chars": 65536, + "count": 205, + "truncated": true, + "expectedParents": 200, + "heldDelta": 13139920, + "endedDelta": 13140016, + "evictedDelta": 20016 + }, + { + "kind": "ring", + "variant": "tail-only", + "chars": 4194304, + "count": 8, + "truncated": true, + "expectedParents": 8, + "heldDelta": 33555760, + "endedDelta": 33555856, + "evictedDelta": 14768 + }, + { + "kind": "tail", + "variant": "fixed-buffer", + "chars": 65536, + "count": 10, + "heldDelta": 22392, + "endedDelta": 24808 + }, + { + "kind": "tail", + "variant": "fixed-buffer", + "chars": 4194304, + "count": 8, + "heldDelta": 13120, + "endedDelta": 15808 + }, + { + "kind": "ring", + "variant": "fixed-buffer", + "chars": 65536, + "count": 205, + "truncated": false, + "expectedParents": 200, + "heldDelta": 36832, + "endedDelta": 36928, + "evictedDelta": 27344 + }, + { + "kind": "ring", + "variant": "fixed-buffer", + "chars": 4194304, + "count": 8, + "truncated": false, + "expectedParents": 8, + "heldDelta": 1056, + "endedDelta": 1152, + "evictedDelta": 14752 + }, + { + "kind": "ring", + "variant": "fixed-buffer", + "chars": 65536, + "count": 205, + "truncated": true, + "expectedParents": 200, + "heldDelta": 3305736, + "endedDelta": 3305832, + "evictedDelta": 28928 + }, + { + "kind": "ring", + "variant": "fixed-buffer", + "chars": 4194304, + "count": 8, + "truncated": true, + "expectedParents": 8, + "heldDelta": 127904, + "endedDelta": 128000, + "evictedDelta": 10784 + }, + { + "kind": "tail", + "variant": "fixed-fallback", + "chars": 65536, + "count": 10, + "heldDelta": 14360, + "endedDelta": 19176 + }, + { + "kind": "tail", + "variant": "fixed-fallback", + "chars": 4194304, + "count": 8, + "heldDelta": 11168, + "endedDelta": 17664 + }, + { + "kind": "ring", + "variant": "fixed-fallback", + "chars": 65536, + "count": 205, + "truncated": false, + "expectedParents": 200, + "heldDelta": 36832, + "endedDelta": 36928, + "evictedDelta": 27344 + }, + { + "kind": "ring", + "variant": "fixed-fallback", + "chars": 4194304, + "count": 8, + "truncated": false, + "expectedParents": 8, + "heldDelta": 1056, + "endedDelta": 1152, + "evictedDelta": 14752 + }, + { + "kind": "ring", + "variant": "fixed-fallback", + "chars": 65536, + "count": 205, + "truncated": true, + "expectedParents": 200, + "heldDelta": 3300216, + "endedDelta": 3300312, + "evictedDelta": 23440 + }, + { + "kind": "ring", + "variant": "fixed-fallback", + "chars": 4194304, + "count": 8, + "truncated": true, + "expectedParents": 8, + "heldDelta": 127904, + "endedDelta": 128000, + "evictedDelta": 10784 + } + ] +} diff --git a/docs/audits/plugin-worker-output-retention/reproduce.cjs b/docs/audits/plugin-worker-output-retention/reproduce.cjs new file mode 100644 index 00000000000..7af9cb5fea5 --- /dev/null +++ b/docs/audits/plugin-worker-output-retention/reproduce.cjs @@ -0,0 +1,249 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const { PassThrough } = require('node:stream') +const { once, EventEmitter } = require('node:events') +const { load, loadSources, sha, read } = require('./sources.cjs') + +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function') +const suffix = 'retained-output-tail' + +async function heap() { + ;/reset/.test('reset') + for (let i = 0; i < 4; i++) { + await new Promise((resolve) => setImmediate(resolve)) + global.gc() + } + return process.memoryUsage().heapUsed +} + +function emitChunk(stream, chars, index, complete, truncated = false) { + if (truncated) { + stream.write(`${index.toString().padStart(4, '0')}${'x'.repeat(chars - 5)}\n`) + return + } + const label = `${index.toString().padStart(4, '0')}:${suffix}` + const final = `\n${label}${complete ? '\n' : ''}` + const text = `${' '.repeat(chars - final.length)}${final}` + stream.write(text) +} + +async function tail(api, variant, chars, count) { + const streams = [] + const start = await heap() + for (let i = 0; i < count; i++) { + const stream = new PassThrough() + api.pipePluginWorkerOutput(stream, 'info', () => {}) + emitChunk(stream, chars, i, false) + streams.push(stream) + } + const heldDelta = (await heap()) - start + const retains = variant === 'before' + assert.ok( + retains ? heldDelta > chars * count * 0.75 : heldDelta < 768 * 1024, + JSON.stringify({ variant, kind: 'tail', chars, count, heldDelta }) + ) + for (const stream of streams) { + const ended = once(stream, 'end') + stream.end() + await ended + } + const endedDelta = (await heap()) - start + assert.ok(endedDelta < 768 * 1024, JSON.stringify({ variant, endedDelta })) + return { kind: 'tail', variant, chars, count, heldDelta, endedDelta } +} + +function verifyRing(log, count, truncated) { + assert.equal(log.get('plugin').length, Math.min(count, 200)) + for (const [index, row] of log.get('plugin').entries()) { + const inputIndex = index + Math.max(0, count - 200) + assert.equal(row.level, 'info') + assert.equal( + row.line, + truncated + ? `${inputIndex.toString().padStart(4, '0')}${'x'.repeat(8192 - 4 - '… [truncated]'.length)}… [truncated]` + : `${inputIndex.toString().padStart(4, '0')}:${suffix}` + ) + } +} + +async function ring(api, variant, chars, count, truncated = false) { + const log = new api.PluginLogBuffer() + const stream = new PassThrough() + api.pipePluginWorkerOutput(stream, 'info', (level, line) => log.append('plugin', level, line)) + const start = await heap() + for (let i = 0; i < count; i++) { + emitChunk(stream, chars, i, true, truncated) + } + assert.equal(log.get('plugin').length, Math.min(count, 200)) + const heldDelta = (await heap()) - start + const retains = variant === 'before' || variant === 'tail-only' + const expectedParents = Math.min(count, 200) + const fixedBudget = expectedParents * (truncated ? 20 * 1024 : 0) + 768 * 1024 + assert.ok( + retains ? heldDelta > chars * expectedParents * 0.75 : heldDelta < fixedBudget, + JSON.stringify({ variant, kind: 'ring', chars, count, truncated, heldDelta }) + ) + const ended = once(stream, 'end') + stream.end() + await ended + const endedDelta = (await heap()) - start + assert.ok(retains ? endedDelta > chars * expectedParents * 0.75 : endedDelta < fixedBudget) + for (let i = 0; i < 200; i++) { + log.append('plugin', 'info', 'replacement') + } + const evictedDelta = (await heap()) - start + assert.ok(evictedDelta < 768 * 1024, JSON.stringify({ variant, evictedDelta })) + assert.equal(log.get('plugin').length, 200) + return { + kind: 'ring', + variant, + chars, + count, + truncated, + expectedParents, + heldDelta, + endedDelta, + evictedDelta + } +} + +async function behavior(api) { + const lines = [] + const stream = new PassThrough() + api.pipePluginWorkerOutput(stream, 'error', (level, line) => lines.push([level, line])) + for (const chunk of [' \nhello', ' world\n', 'x'.repeat(8193), 'discarded', '\nok\n', suffix]) { + stream.write(chunk) + } + const ended = once(stream, 'end') + stream.end() + await ended + assert.equal(lines.length, 4) + assert.deepEqual(lines[0], ['error', 'hello world']) + assert.equal(lines[1][1].length, 8192) + assert.ok(lines[1][1].endsWith('… [truncated]')) + assert.deepEqual(lines[2], ['error', 'ok']) + assert.deepEqual(lines[3], ['error', suffix]) + const unicode = [] + const direct = new EventEmitter() + direct.setEncoding = (encoding) => assert.equal(encoding, 'utf8') + api.pipePluginWorkerOutput(null, 'info', () => assert.fail('Null stream emitted')) + api.pipePluginWorkerOutput(direct, 'info', (level, line) => unicode.push([level, line])) + for (const chunk of [ + '', + ' \r\n', + 'short\n', + 'twelve chars\n', + '\ud800a\udfff\u0000\u6f22\n', + '😀'.repeat(4096), + '\n', + `${'a'.repeat(8191)}\ud800`, + '\udfff\n', + 'q'.repeat(8193), + 'still discarding', + '\nnext\r\n', + 'unterminated 😀' + ]) { + direct.emit('data', chunk) + } + direct.emit('end') + assert.equal(unicode.length, 8) + assert.deepEqual(unicode[2], ['info', '\ud800a\udfff\u0000\u6f22']) + assert.deepEqual(unicode[3], ['info', '😀'.repeat(4096)]) + assert.equal(unicode[4][1].length, 8192) + assert.ok(unicode[4][1].endsWith('… [truncated]')) + assert.deepEqual(unicode[6], ['info', 'next\r']) + assert.deepEqual(unicode[7], ['info', 'unterminated 😀']) + // Keep value comparisons outside heap controls: they can flatten cons strings. + for (const truncated of [false, true]) { + const log = new api.PluginLogBuffer() + const ringStream = new PassThrough() + api.pipePluginWorkerOutput(ringStream, 'info', (level, line) => + log.append('plugin', level, line) + ) + for (let i = 0; i < 3; i++) { + emitChunk(ringStream, 16 * 1024, i, true, truncated) + } + verifyRing(log, 3, truncated) + const ringEnded = once(ringStream, 'end') + ringStream.end() + await ringEnded + } + return { lines, unicode } +} + +async function main() { + const reports = [], + bundles = {}, + behaviors = {} + for (const variant of ['before', 'tail-only', 'fixed-buffer', 'fixed-fallback']) { + const api = await load(variant) + bundles[variant] = api.provenance + if (variant !== 'before') { + api.resetOwnRetainedStringCopier() + const originalBuffer = globalThis.Buffer + try { + if (variant === 'fixed-fallback') { + globalThis.Buffer = undefined + } + assert.equal(api.ownRetainedString(suffix), suffix) + } finally { + globalThis.Buffer = originalBuffer + } + } + behaviors[variant] = await behavior(api) + reports.push(await tail(api, variant, 64 * 1024, 10)) + reports.push(await tail(api, variant, 4 * 1024 * 1024, 8)) + reports.push(await ring(api, variant, 64 * 1024, 205)) + reports.push(await ring(api, variant, 4 * 1024 * 1024, 8)) + reports.push(await ring(api, variant, 64 * 1024, 205, true)) + reports.push(await ring(api, variant, 4 * 1024 * 1024, 8, true)) + } + assert.deepEqual(behaviors.before, behaviors['tail-only']) + assert.deepEqual(behaviors.before, behaviors['fixed-buffer']) + assert.deepEqual(behaviors.before, behaviors['fixed-fallback']) + const normalSources = loadSources() + let crlfReads = 0 + const crlfSources = loadSources((file) => { + crlfReads += 1 + return read(file).replaceAll('\n', '\r\n') + }) + assert.deepEqual(crlfSources, normalSources) + const args = process.argv.slice(2) + assert.ok(args.length === 0 || (args.length === 2 && args[0] === '--output')) + const output = + args.length === 2 + ? path.resolve(args[1]) + : path.join(__dirname, `${process.versions.electron ? 'electron' : 'node'}-results.json`) + const artifactHashes = Object.fromEntries( + ['reproduce.cjs', 'sources.cjs', 'source-versions.json', 'fix.patch'].map((file) => [ + file, + sha(read(path.join(__dirname, file))) + ]) + ) + fs.writeFileSync( + output, + `${JSON.stringify( + { + runtime: process.versions, + artifactHashes, + crlfLoaderControl: { reads: crlfReads, identical: true }, + bundles, + behaviors, + reports + }, + null, + 2 + )}\n` + ) + console.log(JSON.stringify({ output, passed: reports.length })) +} +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) +setTimeout(() => { + console.error('deadline') + process.exit(2) +}, 30000).unref() diff --git a/docs/audits/plugin-worker-output-retention/source-versions.json b/docs/audits/plugin-worker-output-retention/source-versions.json new file mode 100644 index 00000000000..424395b20c5 --- /dev/null +++ b/docs/audits/plugin-worker-output-retention/source-versions.json @@ -0,0 +1,89 @@ +{ + "publicationTopic": "np-oom-scan-retained-text-slices", + "baselineSha256": "969daeddb4f4fde9d53e80b008438aa2942526af940ae9eb4f08cd9440a0f914", + "fixedSha256": "4c7fd9e66d1b2aa948dab72196c3e6fd9df7c646dcf0a15279ee5520bb0a2a7b", + "tailOnlySha256": "5d68af2f66e1466b5a642fa9bb301b8f04ccd2c2741dd5731faac7a2f30c5be5", + "dependencies": { + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "namedRevisions": { + "HEAD": { + "revision": "2e83de3154c4ee1bbeea816734b892c34500a5cc", + "sources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "969daeddb4f4fde9d53e80b008438aa2942526af940ae9eb4f08cd9440a0f914", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + } + }, + "main": { + "revision": "f78483ec29891ab11f49bb25e6cd628837b1242e", + "sources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "969daeddb4f4fde9d53e80b008438aa2942526af940ae9eb4f08cd9440a0f914", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + } + }, + "np-oom-scan-retained-text-slices": { + "revision": "0d2efbc0d3b4f902b9bc02f5451c6ff4291405cf", + "sources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "969daeddb4f4fde9d53e80b008438aa2942526af940ae9eb4f08cd9440a0f914", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + } + }, + "291b4ddd6f1c1af480169885e0fda7f9c78ff053": { + "revision": "291b4ddd6f1c1af480169885e0fda7f9c78ff053", + "sources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "969daeddb4f4fde9d53e80b008438aa2942526af940ae9eb4f08cd9440a0f914", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + } + }, + "e0826956fcfc532f5a1e55b5e081f2e57e553c43": { + "revision": "e0826956fcfc532f5a1e55b5e081f2e57e553c43", + "sources": { + "src/main/plugins/plugin-worker-output-buffer.ts": "969daeddb4f4fde9d53e80b008438aa2942526af940ae9eb4f08cd9440a0f914", + "src/main/plugins/plugin-log-buffer.ts": "c378984b0435b957265b8071f5d2da8a3cbd659a8d2bea67b363b49e573ffdd8", + "src/main/plugins/plugin-host-process.ts": "825b938a5fe510944c26e9b503afeb3a0da13b570558a111adc2f9edc21cce6b", + "src/main/plugins/plugin-worker-manager.ts": "b95bfac2b674b6ecf6da6ba0d08b98596e67f05bf0dff02e8133049b4d5a1e70", + "src/main/plugins/plugin-worker-startup.ts": "4b1e3ffb800e387a7a6f677ceef27a4d72f1b0aca467b54994f0b4518137fa2e", + "src/main/plugins/plugin-service.ts": "057bcb8af5067112f8aec498ad7824b005dfe9ed7880b3e0745ed54fcfb75420", + "src/shared/plugins/plugin-host-protocol.ts": "6f6ce9cfda1d30879695c7c8979b1d0ae93600a3d4417a6cd3c832235d3ae689", + "src/shared/own-retained-string.ts": null, + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + } + } + }, + "historicalScope": "v1.4.198 has the identical parser, ring and caller modules but lacks own-retained-string.ts. The baseline bundle imports only the unchanged parser/ring. Fixed and tail-only variants use the recorded publication helper; this is not a historical application binary." +} diff --git a/docs/audits/plugin-worker-output-retention/sources.cjs b/docs/audits/plugin-worker-output-retention/sources.cjs new file mode 100644 index 00000000000..54005ea1a69 --- /dev/null +++ b/docs/audits/plugin-worker-output-retention/sources.cjs @@ -0,0 +1,94 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const Module = require('node:module') +const { createHash } = require('node:crypto') +const { build } = require('esbuild') +const { applyPatch, parsePatch, reversePatch } = require('diff') + +const root = path.resolve(__dirname, '../../..') +const sourcePath = 'src/main/plugins/plugin-worker-output-buffer.ts' +const canonicalLf = (value) => value.replaceAll('\r\n', '\n') +const read = (file) => canonicalLf(fs.readFileSync(file, 'utf8')) +const sha = (value) => createHash('sha256').update(value).digest('hex') + +function loadSources(readText = read) { + const versionsText = read(path.join(__dirname, 'source-versions.json')) + const versions = JSON.parse(versionsText) + const patches = parsePatch(canonicalLf(readText(path.join(__dirname, 'fix.patch')))) + assert.equal(patches.length, 1) + assert.equal(patches[0].newFileName, `b/${sourcePath}`) + const current = canonicalLf(readText(path.join(root, sourcePath))) + const before = applyPatch(current, reversePatch(patches[0])) + assert.notEqual(before, false, 'The parser no longer matches the reviewed patch') + assert.equal(sha(before), versions.baselineSha256) + assert.equal(sha(current), versions.fixedSha256) + const checkedSources = { [sourcePath]: sha(current) } + for (const [relative, expected] of Object.entries(versions.dependencies)) { + const actual = sha(canonicalLf(readText(path.join(root, relative)))) + assert.equal(actual, expected, `Reviewed dependency changed: ${relative}`) + checkedSources[relative] = actual + } + return { before, current, checkedSources, versions, versionsSha256: sha(versionsText) } +} + +async function load(variant) { + const checked = loadSources() + let source = variant === 'before' ? checked.before : checked.current + if (variant === 'tail-only') { + source = `import { ownRetainedString } from '../../shared/own-retained-string'\n${checked.before}` + assert.equal(source.split(' buffered += segment').length, 2) + source = source.replace( + ' buffered += segment', + ' buffered += newline === -1 ? ownRetainedString(segment) : segment' + ) + assert.equal(sha(source), checked.versions.tailOnlySha256) + } + const entries = [ + "export { pipePluginWorkerOutput } from './src/main/plugins/plugin-worker-output-buffer'", + "export { PluginLogBuffer } from './src/main/plugins/plugin-log-buffer'" + ] + if (variant !== 'before') { + entries.push( + "export { ownRetainedString, resetOwnRetainedStringCopier } from './src/shared/own-retained-string'" + ) + } + const evaluatedSources = {} + const built = await build({ + stdin: { contents: entries.join('\n'), resolveDir: root }, + platform: 'node', + format: 'cjs', + bundle: true, + write: false, + plugins: [ + { + name: 'hash-fenced-plugin-output', + setup(builder) { + builder.onLoad({ filter: /\.ts$/ }, ({ path: file }) => { + const relative = path.relative(root, file).split(path.sep).join('/') + assert.ok(Object.hasOwn(checked.checkedSources, relative), relative) + const contents = relative === sourcePath ? source : read(file) + evaluatedSources[relative] = sha(contents) + return { contents, loader: 'ts' } + }) + } + } + ] + }) + const filename = path.join(__dirname, `${variant}-bundle.cjs`) + const loaded = new Module(filename, module) + loaded.filename = filename + loaded.paths = Module._nodeModulePaths(root) + loaded._compile(built.outputFiles[0].text, filename) + return { + ...loaded.exports, + provenance: { + checkedSources: checked.checkedSources, + evaluatedSources, + sourceVersionsSha256: checked.versionsSha256, + bundleSha256: sha(built.outputFiles[0].text) + } + } +} + +module.exports = { load, loadSources, sha, read } diff --git a/docs/audits/pty-detector-retention/README.md b/docs/audits/pty-detector-retention/README.md new file mode 100644 index 00000000000..21637ed610a --- /dev/null +++ b/docs/audits/pty-detector-retention/README.md @@ -0,0 +1,59 @@ +# Retained PTY detector input + +The advertised-URL watcher keeps a 4,096-character carry for each bound PTY and +16,384 characters for each of at most 32 unbound PTYs. The Command Code status +detector keeps 300 characters before its agent-specific prefilter, including for +ordinary shell, Claude, and Codex output. Each could keep the whole original +input alive through a V8 sliced string. + +The fix uses the existing `ownRetainedString` copier when dropping oversized +input. It preserves URL reconstruction, status detection, UTF-16 code units, +binding/unbinding, cache limits, and remote/local authority. These are three +additional boundaries in [#20960](https://github.com/stablyai/orca/pull/20960). + +## Reproduce + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc docs/audits/pty-detector-retention/reproduce.mjs +``` + +The script bundles the actual detector and watcher. The baseline removes only +the three copy calls in memory. Each input has its own live owner; URL cases +use separate watcher instances to isolate each carry. Heap is measured after +GC, before completing the partial URLs and verifying cleanup. Results include +owner overhead, not just text. [Bundle hashes and measurements](./results.json). + +| Case | Input per owner | Owners | Heap before | Heap after | +| ------------------- | ---------------: | -----: | ----------: | ---------: | +| Status detector | 64 Ki characters | 32 | 2,112,688 | 41,400 | +| Bound URL carry | 64 Ki characters | 32 | 2,173,848 | 204,112 | +| URL pending binding | 64 Ki characters | 32 | 2,148,824 | 575,512 | +| Status detector | 4 Mi characters | 8 | 33,557,144 | 6,504 | +| Bound URL carry | 4 Mi characters | 8 | 33,569,168 | 47,112 | +| URL pending binding | 4 Mi characters | 8 | 33,568,936 | 144,504 | + +Captured with Node v26.6.0 on macOS. Three GC regression tests failed before the +fix, retaining about 32 MiB each, and pass afterward. The five-suite run passes +164 tests including existing URL/status behavior and copier Unicode/fallback +tests. + +## Scope and limits + +Main feeds both observers before renderer batching. Default daemon bulk output +frames are at most 64 Ki UTF-16 characters; main's later 16 Ki-character batching +does not bound these readers. The 4 Mi-character cases demonstrate the retaining +mechanism under larger inputs, not normal daemon frame size. Both implementations +also exist in `v1.4.198`. + +Transformed frames bypass ordinary chunk slicing but still face the daemon's +16 MiB encoded-line limit. Native fallback output has no application chunk cap; +this audit does not establish multi-MiB native reads. Ordinary relay chunks are +16 Ki characters. The actual main feed is `orca-runtime-on-pty-data.ts` and the +ordinary daemon bound is in `daemon-stream-data-batcher.ts`. + +These are per-owner last-input costs, not unbounded growth for a fixed set of +PTYs and fixed-size frames. Owners consuming the same input can share the same +backing string, so the three measurements must not be added as independent +process costs. Unbind removes URL buffers and pending entries. This improves +memory proportional to active owners; it does not establish the cause or growth +rate of #19831 or #19768. Copy work is bounded by the small retained tails. diff --git a/docs/audits/pty-detector-retention/reproduce.mjs b/docs/audits/pty-detector-retention/reproduce.mjs new file mode 100644 index 00000000000..b6c2f3a8c9e --- /dev/null +++ b/docs/audits/pty-detector-retention/reproduce.mjs @@ -0,0 +1,144 @@ +import assert from 'node:assert/strict' +import { createHash } from 'node:crypto' +import { readFile } from 'node:fs/promises' +import { fileURLToPath } from 'node:url' +import { build } from 'esbuild' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1' || typeof global.gc !== 'function') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1 node --expose-gc') +} +const root = fileURLToPath(new URL('../../../', import.meta.url)) +const replacements = { + 'command-code-output-status.ts': [ + 'ownRetainedString(data.slice(-RECENT_TEXT_LIMIT))', + 'data.slice(-RECENT_TEXT_LIMIT)' + ], + 'advertised-url-parsing.ts': [ + 'ownRetainedString(chunk.slice(-PER_PTY_BUFFER_LIMIT))', + 'chunk.slice(-PER_PTY_BUFFER_LIMIT)' + ], + 'advertised-url-watcher.ts': [ + 'ownRetainedString(combined.slice(-PENDING_PRE_BIND_LIMIT))', + 'combined.slice(-PENDING_PRE_BIND_LIMIT)' + ] +} +const results = [] +const bundles = {} + +function heapAfterGc() { + global.gc() + global.gc() + return process.memoryUsage().heapUsed +} + +function measure(makeOwner, validate, inputChars, count) { + const before = heapAfterGc() + const owners = Array.from({ length: count }, (_, index) => { + const prefix = `${index}:` + const suffix = `\nhttp://localhost:${4100 + index}` + return makeOwner( + `${prefix}${'x'.repeat(inputChars - prefix.length - suffix.length)}${suffix}`, + index + ) + }) + const heapDelta = heapAfterGc() - before + owners.forEach(validate) + return { inputChars, count, heapDelta } +} + +for (const fixed of [false, true]) { + const result = await build({ + stdin: { + contents: ` + export { createCommandCodeOutputStatusDetector } from './src/shared/command-code-output-status' + export { AdvertisedUrlWatcher } from './src/main/ports/advertised-url-watcher' + `, + resolveDir: root, + loader: 'ts' + }, + bundle: true, + platform: 'node', + format: 'esm', + write: false, + plugins: fixed + ? [] + : [ + { + name: 'baseline-without-detector-tail-copy', + setup(builder) { + builder.onLoad( + { + filter: + /(?:command-code-output-status|advertised-url-parsing|advertised-url-watcher)\.ts$/ + }, + async ({ path }) => { + const source = await readFile(path, 'utf8') + const replacement = Object.entries(replacements).find(([name]) => + path.endsWith(name) + )?.[1] + if (!replacement || !source.includes(replacement[0])) { + throw new Error('The copy boundary changed; update the baseline transform') + } + return { contents: source.replaceAll(...replacement), loader: 'ts' } + } + ) + } + } + ] + }) + const bundle = result.outputFiles[0].text + bundles[fixed ? 'after' : 'before'] = createHash('sha256').update(bundle).digest('hex') + const { createCommandCodeOutputStatusDetector, AdvertisedUrlWatcher } = await import( + `data:text/javascript;base64,${Buffer.from(bundle).toString('base64')}` + ) + for (const [inputChars, count] of [ + [64 * 1024, 32], + [4 * 1024 * 1024, 8] + ]) { + results.push({ + kind: 'command-code-detector', + fixed, + ...measure( + (data) => { + const detector = createCommandCodeOutputStatusDetector({ onWorking: () => {} }) + detector.observe(data) + return detector + }, + (detector) => assert.equal(detector.observe('\nordinary output\n'), false), + inputChars, + count + ) + }) + for (const bound of [true, false]) { + results.push({ + kind: bound ? 'url-bound-pty' : 'url-before-binding', + fixed, + ...measure( + (data) => { + const watcher = new AdvertisedUrlWatcher() + if (bound) { + watcher.bindPty('pty', 'workspace') + } + watcher.ingest('pty', data) + return watcher + }, + (watcher, index) => { + watcher.bindPty('pty', 'workspace') + watcher.ingest('pty', '/\n') + assert.equal( + watcher.lookup('workspace', 4100 + index)?.origin, + `http://localhost:${4100 + index}` + ) + watcher.unbindPty('pty') + assert.equal(watcher.lookup('workspace', 4100 + index), undefined) + }, + inputChars, + count + ) + }) + } + } +} +console.log( + JSON.stringify({ node: process.version, platform: process.platform, bundles, results }, null, 2) +) diff --git a/docs/audits/pty-detector-retention/results.json b/docs/audits/pty-detector-retention/results.json new file mode 100644 index 00000000000..5bfdc48c4e8 --- /dev/null +++ b/docs/audits/pty-detector-retention/results.json @@ -0,0 +1,94 @@ +{ + "node": "v26.6.0", + "platform": "darwin", + "bundles": { + "before": "139646a3dc9316a104f472c86e674be062da8fbdf5a35563624557d44d27f471", + "after": "a2efe55354954f78e187fd65a247b51d97017776892979b631725ff686065168" + }, + "results": [ + { + "kind": "command-code-detector", + "fixed": false, + "inputChars": 65536, + "count": 32, + "heapDelta": 2112688 + }, + { + "kind": "url-bound-pty", + "fixed": false, + "inputChars": 65536, + "count": 32, + "heapDelta": 2173848 + }, + { + "kind": "url-before-binding", + "fixed": false, + "inputChars": 65536, + "count": 32, + "heapDelta": 2148824 + }, + { + "kind": "command-code-detector", + "fixed": false, + "inputChars": 4194304, + "count": 8, + "heapDelta": 33557144 + }, + { + "kind": "url-bound-pty", + "fixed": false, + "inputChars": 4194304, + "count": 8, + "heapDelta": 33569168 + }, + { + "kind": "url-before-binding", + "fixed": false, + "inputChars": 4194304, + "count": 8, + "heapDelta": 33568936 + }, + { + "kind": "command-code-detector", + "fixed": true, + "inputChars": 65536, + "count": 32, + "heapDelta": 41400 + }, + { + "kind": "url-bound-pty", + "fixed": true, + "inputChars": 65536, + "count": 32, + "heapDelta": 204112 + }, + { + "kind": "url-before-binding", + "fixed": true, + "inputChars": 65536, + "count": 32, + "heapDelta": 575512 + }, + { + "kind": "command-code-detector", + "fixed": true, + "inputChars": 4194304, + "count": 8, + "heapDelta": 6504 + }, + { + "kind": "url-bound-pty", + "fixed": true, + "inputChars": 4194304, + "count": 8, + "heapDelta": 47112 + }, + { + "kind": "url-before-binding", + "fixed": true, + "inputChars": 4194304, + "count": 8, + "heapDelta": 144504 + } + ] +} diff --git a/docs/audits/retained-text-slices/README.md b/docs/audits/retained-text-slices/README.md new file mode 100644 index 00000000000..15bc2b39145 --- /dev/null +++ b/docs/audits/retained-text-slices/README.md @@ -0,0 +1,75 @@ +# Retained CI and terminal text tails + +Capped V8 string slices can keep their entire original input alive. The affected +CI excerpt cache accepts 128 entries of 16 KiB text, from downloads up to 64 MiB. +GitLab's raw-trace clamp reaches the same shared excerpt function. Terminal +session/eager/shutdown buffers, deferred reattach queues, recent-output buffers, +and error surfaces also retained oversized parents despite their logical caps. + +The fix reuses the existing shared `ownRetainedString` copier for CI, persisted +session tails, and main/relay recent output. Renderer queues and errors reuse +their existing `flattenRetainedSlice` helper. Content, Unicode, earlier-error +selection, cache counts, and transport payloads stay identical. Ordinary +untruncated terminal chunks keep their existing path. Main/relay recent output +preserves chunk boundaries for path-candidate backfill. + +Local persisted scrollback is already pruned; the session-buffer fix primarily +covers remote or not-yet-classified owners. Queue and error fixes cover local +and remote output. The main/relay recent-output buffer has a configurable cap, +64 Ki characters by default. + +## Reproduce + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc docs/audits/retained-text-slices/reproduce.mjs +``` + +The script bundles actual production functions. Its baseline removes only the +six new copy boundaries in memory; production files are not changed. Each case +retains eight distinct inputs: 2 Mi characters per CI log and 4 Mi characters +per terminal input. It measures heap after GC and clears V8's independent legacy +RegExp input reference. Bundle hashes and measurements are in +[results.json](./results.json). + +| Case | Returned bytes, all eight | Retained heap before | After | +| -------------------------------- | ------------------------: | -------------------: | --------: | +| GitHub long line | 131,072 | 16,787,512 | 145,224 | +| GitHub earlier Unicode error | 131,064 | 33,577,840 | 112,744 | +| GitLab long line | 131,072 | 16,793,640 | 147,312 | +| Persisted terminal buffers | 4,194,304 | 33,555,624 | 4,195,032 | +| Eager/pre-handler/shutdown tails | 4,194,304 | 33,556,040 | 4,202,608 | +| Main/relay recent output | 524,288 | 33,558,752 | 527,384 | +| Terminal error surfaces | 32,000 | 33,555,864 | 33,336 | +| Deferred reattach tails | 4,194,304 | 33,565,384 | 4,198,352 | + +Captured on macOS with Node v26.6.0. Heap samples include allocator/GC variation; +the large separation is the relevant result. Regression tests also retain the +actual error state and shutdown/reattach/recent-output queue objects. + +Validation passed: 41 tests across six CI/provider/helper suites; 69 tests across +six terminal storage/ownership/UTF-8 suites; 28 tests across three error/reattach +suites; and a final 63 tests across seven recent-output/CI/terminal/copier suites. +These are per-run counts and overlap. Full typecheck and changed-code quality pass. + +All six cap/slice paths exist in `v1.4.198`. Neither #19831 nor #19768 establishes +the CI-log viewing or oversized terminal inputs required for incident attribution. +Copying costs scale with retained caps: 16 KiB per CI excerpt, 4,000 characters +per error, 512 KiB for the largest byte-capped buffer, and 512 Ki characters for +deferred reattach. The change does not reduce temporary original-input allocation. + +The follow-up [PTY detector reproduction](../pty-detector-retention/README.md) +adds three boundaries in the same PR: advertised-URL carries, output waiting for +workspace binding, and Command Code status carries used by ordinary PTYs too. +Thirty-two production-sized 64 Ki-character inputs retain about 2.1 MB in each +isolated baseline. Owned carries reduce that to about 41 KB, 204 KB, or 575 KB, +including the different owner objects. These per-owner costs are not an +unbounded growth curve, and readers of the same input can share its parent. +The follow-up adds 164 passing tests across five detector/URL/copier suites. + +The [Claude task metadata reproduction](../claude-task-retention/README.md) adds +the shared 512-character description/name boundary. JSON-parsed task frames +retained their parents in the actual live, settled, and recently removed tracker +entries. Eight 4 Mi-character inputs retained about 32 MiB before the copy and +7–12 KB afterward; 32 smaller 64 Ki-character inputs retained about 2.1 MB before +and 25–45 KB afterward. These synthetic fields establish a retaining mechanism, +not the trigger of a reported incident. diff --git a/docs/audits/retained-text-slices/reproduce.mjs b/docs/audits/retained-text-slices/reproduce.mjs new file mode 100644 index 00000000000..86b5e818e8b --- /dev/null +++ b/docs/audits/retained-text-slices/reproduce.mjs @@ -0,0 +1,161 @@ +import { createHash } from 'node:crypto' +import { readFile } from 'node:fs/promises' +import { fileURLToPath } from 'node:url' +import { build } from 'esbuild' + +if (process.env.ORCA_BACKGROUND_LAUNCH !== '1' || typeof global.gc !== 'function') { + throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1 node --expose-gc') +} +const root = fileURLToPath(new URL('../../../', import.meta.url)) +const replacements = { + 'recent-pty-output-buffer.ts': [ + 'this.chunks = [data.length > this.limit ? ownRetainedString(data.slice(-this.limit)) : data]', + 'this.chunks = [data.slice(-this.limit)]' + ], + 'check-job-log-tail-slice.ts': [ + 'return ownRetainedString(buildCheckLogTail(logText))', + 'return buildCheckLogTail(logText)' + ], + 'workspace-session-terminal-buffers.ts': [ + 'return ownRetainedString(\n clampUtf8TextTail(buffer, TERMINAL_SCROLLBACK_SESSION_BUFFER_BYTE_LIMIT).text\n )', + 'return clampUtf8TextTail(buffer, TERMINAL_SCROLLBACK_SESSION_BUFFER_BYTE_LIMIT).text' + ], + 'pty-eager-buffer-clamp.ts': [ + 'data: tail.text.length < data.length ? flattenRetainedSlice(tail.text) : tail.text', + 'data: tail.text' + ], + 'terminal-error-accumulation.ts': ['return flattenRetainedSlice(bounded)', 'return bounded'], + 'deferred-reattach-live-data-queue.ts': [ + 'flattenRetainedSlice(chunk.data.slice(-MAX_DEFERRED_REATTACH_LIVE_CHARS))', + 'chunk.data.slice(-MAX_DEFERRED_REATTACH_LIVE_CHARS)' + ] +} +const results = [] +const bundles = {} +const parentChars = 2 * 1024 * 1024 +const count = 8 + +function measure(excerpt, makeLog) { + global.gc() + const before = process.memoryUsage().heapUsed + const retained = Array.from({ length: count }, (_, index) => excerpt(makeLog(index))) + // Clear V8's independent legacy RegExp input reference before measuring our retained values. + void /probe/.test('probe') + global.gc() + global.gc() + const heapDelta = process.memoryUsage().heapUsed - before + return { + entries: retained.length, + logicalChars: retained.reduce((total, text) => total + text.length, 0), + logicalBytes: retained.reduce((total, text) => total + Buffer.byteLength(text), 0), + heapDelta + } +} + +for (const fixed of [false, true]) { + const result = await build({ + stdin: { + contents: ` + export { RecentPtyOutputBuffer } from './src/main/runtime/recent-pty-output-buffer' + export { sliceCheckLogTail } from './src/shared/check-job-log-tail-slice' + export { gitLabJobTraceToLogExcerpt } from './src/shared/gitlab-job-log-excerpt' + export { capTerminalScrollbackSessionBuffer } from './src/shared/workspace-session-terminal-buffers' + export { clampUtf8Tail } from './src/renderer/src/components/terminal-pane/pty-eager-buffer-clamp' + export { boundTerminalErrorSurface } from './src/renderer/src/components/terminal-pane/terminal-error-accumulation' + export { DeferredReattachLiveDataQueue } from './src/renderer/src/components/terminal-pane/deferred-reattach-live-data-queue' + `, + resolveDir: root, + loader: 'ts' + }, + bundle: true, + platform: 'node', + format: 'esm', + write: false, + plugins: fixed + ? [] + : [ + { + name: 'baseline-without-retained-tail-copy', + setup(builder) { + builder.onLoad( + { + filter: + /(?:recent-pty-output-buffer|check-job-log-tail-slice|workspace-session-terminal-buffers|pty-eager-buffer-clamp|terminal-error-accumulation|deferred-reattach-live-data-queue)\.ts$/ + }, + async ({ path }) => { + const source = await readFile(path, 'utf8') + const replacement = Object.entries(replacements).find(([name]) => + path.endsWith(name) + )?.[1] + if (!replacement || !source.includes(replacement[0])) { + throw new Error('The copy boundary changed; update the baseline transform') + } + return { + contents: source.replaceAll(...replacement), + loader: 'ts' + } + } + ) + } + } + ] + }) + const bundle = result.outputFiles[0].text + bundles[fixed ? 'after' : 'before'] = createHash('sha256').update(bundle).digest('hex') + const { + sliceCheckLogTail, + gitLabJobTraceToLogExcerpt, + capTerminalScrollbackSessionBuffer, + clampUtf8Tail, + boundTerminalErrorSurface, + DeferredReattachLiveDataQueue, + RecentPtyOutputBuffer + } = await import(`data:text/javascript;base64,${Buffer.from(bundle).toString('base64')}`) + for (const [kind, makeLog, excerpt] of [ + ['github-long-line', (i) => `${i}:${'x'.repeat(parentChars)}`, sliceCheckLogTail], + [ + 'github-earlier-error', + (i) => `error: ${i}:${'界'.repeat(parentChars)}\n${'recent\n'.repeat(100)}`, + sliceCheckLogTail + ], + ['gitlab-long-line', (i) => `${i}:${'x'.repeat(parentChars)}`, gitLabJobTraceToLogExcerpt], + [ + 'terminal-session-buffer', + (i) => `${i}:${'x'.repeat(parentChars * 2)}`, + capTerminalScrollbackSessionBuffer + ], + [ + 'terminal-eager-buffer', + (i) => `${i}:${'x'.repeat(parentChars * 2)}`, + (text) => clampUtf8Tail(text, 512 * 1024).data + ], + [ + 'terminal-recent-output', + (i) => `${i}:${'x'.repeat(parentChars * 2)}`, + (data) => { + const buffer = new RecentPtyOutputBuffer() + buffer.append(data) + return buffer.read() + } + ], + ['terminal-error', (i) => `${'x'.repeat(parentChars * 2)}:${i}`, boundTerminalErrorSurface], + [ + 'terminal-deferred-reattach', + (i) => `${i}:${'x'.repeat(parentChars * 2)}`, + (data) => { + const queue = new DeferredReattachLiveDataQueue() + queue.enqueue({ data, ptyId: 'p', streamGeneration: 1 }) + return queue.takeAll()[0].data + } + ] + ]) { + results.push({ kind, fixed, ...measure(excerpt, makeLog) }) + } +} +console.log( + JSON.stringify( + { node: process.version, platform: process.platform, parentChars, count, bundles, results }, + null, + 2 + ) +) diff --git a/docs/audits/retained-text-slices/results.json b/docs/audits/retained-text-slices/results.json new file mode 100644 index 00000000000..b16d4ab323a --- /dev/null +++ b/docs/audits/retained-text-slices/results.json @@ -0,0 +1,140 @@ +{ + "node": "v26.6.0", + "platform": "darwin", + "parentChars": 2097152, + "count": 8, + "bundles": { + "before": "b1f295283888921d8d473649185b5146ed8379bc8344b2bf04a0a4fcf334b5ec", + "after": "74444cedf1564c7092a46058f331ad2acc55b222b09340d5a19ca5a76de611fb" + }, + "results": [ + { + "kind": "github-long-line", + "fixed": false, + "entries": 8, + "logicalChars": 131072, + "logicalBytes": 131072, + "heapDelta": 16787512 + }, + { + "kind": "github-earlier-error", + "fixed": false, + "entries": 8, + "logicalChars": 43816, + "logicalBytes": 131064, + "heapDelta": 33577840 + }, + { + "kind": "gitlab-long-line", + "fixed": false, + "entries": 8, + "logicalChars": 131072, + "logicalBytes": 131072, + "heapDelta": 16793640 + }, + { + "kind": "terminal-session-buffer", + "fixed": false, + "entries": 8, + "logicalChars": 4194304, + "logicalBytes": 4194304, + "heapDelta": 33555624 + }, + { + "kind": "terminal-eager-buffer", + "fixed": false, + "entries": 8, + "logicalChars": 4194304, + "logicalBytes": 4194304, + "heapDelta": 33556040 + }, + { + "kind": "terminal-recent-output", + "fixed": false, + "entries": 8, + "logicalChars": 524288, + "logicalBytes": 524288, + "heapDelta": 33558752 + }, + { + "kind": "terminal-error", + "fixed": false, + "entries": 8, + "logicalChars": 32000, + "logicalBytes": 32000, + "heapDelta": 33555864 + }, + { + "kind": "terminal-deferred-reattach", + "fixed": false, + "entries": 8, + "logicalChars": 4194304, + "logicalBytes": 4194304, + "heapDelta": 33565384 + }, + { + "kind": "github-long-line", + "fixed": true, + "entries": 8, + "logicalChars": 131072, + "logicalBytes": 131072, + "heapDelta": 145224 + }, + { + "kind": "github-earlier-error", + "fixed": true, + "entries": 8, + "logicalChars": 43816, + "logicalBytes": 131064, + "heapDelta": 112744 + }, + { + "kind": "gitlab-long-line", + "fixed": true, + "entries": 8, + "logicalChars": 131072, + "logicalBytes": 131072, + "heapDelta": 147312 + }, + { + "kind": "terminal-session-buffer", + "fixed": true, + "entries": 8, + "logicalChars": 4194304, + "logicalBytes": 4194304, + "heapDelta": 4195032 + }, + { + "kind": "terminal-eager-buffer", + "fixed": true, + "entries": 8, + "logicalChars": 4194304, + "logicalBytes": 4194304, + "heapDelta": 4202608 + }, + { + "kind": "terminal-recent-output", + "fixed": true, + "entries": 8, + "logicalChars": 524288, + "logicalBytes": 524288, + "heapDelta": 527384 + }, + { + "kind": "terminal-error", + "fixed": true, + "entries": 8, + "logicalChars": 32000, + "logicalBytes": 32000, + "heapDelta": 33336 + }, + { + "kind": "terminal-deferred-reattach", + "fixed": true, + "entries": 8, + "logicalChars": 4194304, + "logicalBytes": 4194304, + "heapDelta": 4198352 + } + ] +} diff --git a/docs/audits/terminal-mode-tail-retention/README.md b/docs/audits/terminal-mode-tail-retention/README.md new file mode 100644 index 00000000000..df5635a6f3a --- /dev/null +++ b/docs/audits/terminal-mode-tail-retention/README.md @@ -0,0 +1,122 @@ +# Retained terminal mode scan tails + +The kitty keyboard tracker and daemon mouse-mode mirror retain an incomplete +escape-sequence tail of at most 4,096 UTF-16 code units. A V8 sliced string can +keep the entire consumed input alive through that small tail. An ordinary +split grouped mode sequence, `ESC[?1049;2004;1000;`, is enough: its 18-character +tail retains each input backing string while its parser stays idle. + +The correction copies only accepted incomplete tails through the existing +`ownRetainedString` helper. Empty/rejected tails and short ESC/CSI prefixes keep +their existing behavior; the helper leaves strings shorter than 13 code units +alone. Parser state, live/replay semantics, stack caps, mode flags, and wire +content are unchanged. These are additional boundaries in +[#20960](https://github.com/stablyai/orca/pull/20960), alongside the +[PTY detector carries](../pty-detector-retention/README.md). + +## Reproduce + +```sh +ORCA_BACKGROUND_LAUNCH=1 node --expose-gc --max-old-space-size=192 docs/audits/terminal-mode-tail-retention/reproduce.cjs +``` + +Run the same script with the installed Electron executable, setting +`ELECTRON_RUN_AS_NODE=1` and `ORCA_BACKGROUND_LAUNCH=1`, and passing the same +Node flags. This launches no app window or native PTY. Each run has a 30-second +deadline and writes either [Node results](./node-results.json) or +[Electron results](./electron-results.json). + +The loader reads the actual five source modules, verifies their fixed hashes, +reverses only the three copy calls/imports in memory for the baseline, and +verifies the resulting baseline hashes. All evaluated module and bundle hashes +are recorded. It needs no Git history, absolute development paths, or ignored +notes. CRLF source text is normalized before hashing. The parsers and flag +parser match `v1.4.198`; all five modules match the pre-extension topic +`8d599520e44654a5c28e9930e3070c00d6499931`, except for these copy calls. This +tests current dependencies and the selected source modules, not a historical +application binary. See [source versions](./source-versions.json). + +Each runtime checks 42 bounded heap cases: baseline, fixed Buffer copier, and +fixed Bufferless copier; kitty live/replay, mouse live; 32 distinct 64-Ki-character +inputs and eight 4-Mi-character inputs; short, complete, oversized, and C1-CSI +tail controls. Completion must reconstruct the correct modes and release the +large backing strings. Additional controls preserve replay push idempotence, +the 16-frame live stack cap, alternate-screen state, snapshot unknownness, +mouse encodings, and RIS with a trailing partial sequence. + +Both runtimes pass all 42 cases. Representative live-path heap deltas in bytes: + +| Runtime | Parser | Input × owners | Baseline | Buffer copy | Bufferless copy | +| --------------------- | ------ | -------------- | ---------: | ----------: | --------------: | +| Node 26 | Kitty | 64 Ki × 32 | 2,120,952 | 18,376 | 9,480 | +| Node 26 | Mouse | 64 Ki × 32 | 2,111,984 | 15,112 | 9,336 | +| Node 26 | Kitty | 4 Mi × 8 | 33,557,944 | 3,448 | 3,448 | +| Node 26 | Mouse | 4 Mi × 8 | 33,557,072 | 1,312 | 1,312 | +| Electron 43 / Node 24 | Kitty | 64 Ki × 32 | 2,111,864 | 12,244 | 5,192 | +| Electron 43 / Node 24 | Mouse | 64 Ki × 32 | 2,103,444 | 14,432 | 8,004 | +| Electron 43 / Node 24 | Kitty | 4 Mi × 8 | 33,556,316 | 1,884 | 2,604 | +| Electron 43 / Node 24 | Mouse | 4 Mi × 8 | 33,556,172 | 776 | 752 | + +Heap readings include owner overhead and follow forced GC. The harness clears +V8's last successful regexp input identically in baseline and fixed cases to +isolate per-owner storage. That independent process-wide regexp reference can +keep a most-recent input alive until another successful match; this change does +not eliminate it. The Bufferless selection is memoized while Buffer is absent, +then Buffer is restored before measuring; it exercises the actual renderer +fallback without running a browser renderer. + +Two permanent kitty heap regressions fail before the correction at 33,560,832 +and 33,575,040 retained bytes against a 2-MiB limit. They also verify that the +retained prefix completes correctly and that replay/pop/snapshot state remains +valid. Existing parser and copier tests provide the wider protocol controls. +The two mouse regressions likewise fail before the correction at 33,559,240 and +33,573,200 bytes, and pass afterward with both CSI encodings. The five-suite +run passes 98 tests, including actual headless-emulator mode snapshots; Node +and renderer TypeScript checks pass. + +## Callers and lifetime + +- Kitty renderer panes create or reuse one tracker per pane in + `connect-pane-pty.ts:160`. `write-pty-output-to-xterm.ts:23` feeds application + output; `apply-reattach-payload.ts` and `hidden-output-seq-and-skip.ts` feed + replay. Fresh spawn and exit reset it, and + `terminal-pane-pane-closed.ts:69` deletes the map entry. Dashboard previews + own another tracker per effect (`AgentTerminalPreview.tsx:116`); cleanup + removes its listeners and disposes its terminal. +- Main's `orca-runtime-capture-provider-terminal-buffer.ts:23` registers + temporary live scanners during provider snapshot acquisition and removes + them in `finally`. It creates a persistent tracker only after observing an + alternate-screen transition (`:48–57`). `orca-runtime-on-pty-data.ts:30` + feeds those trackers before later output processing. Exit, floating PTY + liveness cleanup, and provider generation reset delete the persistent entry. +- The daemon does not directly instantiate the kitty tracker, despite its + old class comment: its kitty flags come from xterm. No mobile bundle imports + this class. Mobile can exercise main-side snapshot acquisition; SSH output + can reach main and renderer trackers through the existing provider routes. +- Mouse mirrors are owned by `HeadlessEmulator` (`headless-emulator.ts:59`). + Async writes scan after xterm parses the data (`:190`); synchronous live and + cold-restore writes scan at `:224`. Both daemon sessions and main's headless + projections use this emulator. It therefore also covers local/remote host + emulators serving mobile clients. Emulator disposal stops future writes; + eventual owner release removes the mirror. Completing/replacing its tail + also releases the old backing string. No ownership or shutdown rule changes. + +## Scope and limits + +This is a per-owner last-input cost. It does not grow indefinitely with a fixed +set of parsers and bounded input chunks, and further output often completes or +replaces the tail. Multiple readers of the same input can share its backing +storage; do not sum their measurements as independent process memory. + +Ordinary daemon bulk frames delivered to main are at most 64 Ki characters +(`daemon-stream-data-batcher.ts:35`), and ordinary relay output slices are +16 Ki characters (`relay/pty-handler.ts:343`). Mouse scanning inside the daemon +happens before outgoing stream framing. The 64-Ki cases demonstrate the issue +at a normal main-input bound; the 4-Mi cases amplify the mechanism, not a claim +that ordinary native reads or daemon frames have that size. Replay inputs and +transformed streams follow their own existing limits. No network, application +renderer, operating-system PTY, or incident heap was used in this proof. + +This reduces retained output in local and SSH paths without changing published +terminal content. It neither establishes the trigger in #19831/#19768 nor +explains a reported sustained growth rate or multi-gigabyte incident by itself. diff --git a/docs/audits/terminal-mode-tail-retention/electron-results.json b/docs/audits/terminal-mode-tail-retention/electron-results.json new file mode 100644 index 00000000000..e81d9abc27c --- /dev/null +++ b/docs/audits/terminal-mode-tail-retention/electron-results.json @@ -0,0 +1,467 @@ +{ + "node": "v24.21.0", + "electron": "43.7.0", + "v8": "15.0.245.31-electron.0", + "platform": "darwin", + "runnerSha256": "b78dc54a08345732235d4fcc1f72ca1534b00f89280981beda00667d22b8f291", + "loaderSha256": "730da9091abba3997432657aef4a2e6e7a3dd0e9218ea78f1838806b6c0fa789", + "pendingTailCodeUnits": 18, + "clearedRegexStatics": true, + "bundles": { + "baseline": { + "bundleSha256": "6023f5d6868f5db601f6883acda5d56990d204084a6a3c812799aff1e533a5b0", + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/terminal-kitty-keyboard-mode-tracker.ts": "2ee0bad47d4575046f71c16e0334a8ae8be531f0cfc67133abc287f8ae5aa403", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225" + }, + "sourceVersionsSha256": "29cde88cb882b2d7726a2d4a077484217076090acc009f47a805e368f597e51a" + }, + "fixed-buffer": { + "bundleSha256": "bbae10735fc999eea9dafffcd0e1fc4c47e1a8f1daf0045ed074b08cf3a93d45", + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/shared/terminal-kitty-keyboard-mode-tracker.ts": "a8da704564b9ab97aa344f380940021d3fa98f80af817751824d14271acb8a84", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225" + }, + "sourceVersionsSha256": "29cde88cb882b2d7726a2d4a077484217076090acc009f47a805e368f597e51a" + }, + "fixed-fallback": { + "bundleSha256": "bbae10735fc999eea9dafffcd0e1fc4c47e1a8f1daf0045ed074b08cf3a93d45", + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/shared/terminal-kitty-keyboard-mode-tracker.ts": "a8da704564b9ab97aa344f380940021d3fa98f80af817751824d14271acb8a84", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225" + }, + "sourceVersionsSha256": "29cde88cb882b2d7726a2d4a077484217076090acc009f47a805e368f597e51a" + } + }, + "reports": [ + { + "variant": "baseline", + "kind": "kitty", + "method": "scan", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 2111864, + "afterCompletionDelta": 29832 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 33556316, + "afterCompletionDelta": 9244 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scanReplay", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 2102416, + "afterCompletionDelta": 4916 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scanReplay", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 33556304, + "afterCompletionDelta": 1968 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 2, + "heapDelta": 1756, + "afterCompletionDelta": 1712 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 1652, + "afterCompletionDelta": 2880 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 1652, + "afterCompletionDelta": 3024 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 17, + "heapDelta": 33556316, + "afterCompletionDelta": 1692 + }, + { + "variant": "baseline", + "kind": "mouse", + "method": "scan", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 2103444, + "afterCompletionDelta": 5584 + }, + { + "variant": "baseline", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 33556172, + "afterCompletionDelta": 1496 + }, + { + "variant": "baseline", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 2, + "heapDelta": 672, + "afterCompletionDelta": 644 + }, + { + "variant": "baseline", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 520, + "afterCompletionDelta": 532 + }, + { + "variant": "baseline", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 520, + "afterCompletionDelta": 532 + }, + { + "variant": "baseline", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 17, + "heapDelta": 33555224, + "afterCompletionDelta": 560 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scan", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 12244, + "afterCompletionDelta": 11632 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 1884, + "afterCompletionDelta": 1640 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scanReplay", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 5148, + "afterCompletionDelta": 4176 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scanReplay", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 1884, + "afterCompletionDelta": 1640 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 2, + "heapDelta": 1756, + "afterCompletionDelta": 1688 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 1652, + "afterCompletionDelta": 2808 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 1628, + "afterCompletionDelta": 1640 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 17, + "heapDelta": 1884, + "afterCompletionDelta": 1652 + }, + { + "variant": "fixed-buffer", + "kind": "mouse", + "method": "scan", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 14432, + "afterCompletionDelta": 13692 + }, + { + "variant": "fixed-buffer", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 776, + "afterCompletionDelta": 532 + }, + { + "variant": "fixed-buffer", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 2, + "heapDelta": 672, + "afterCompletionDelta": 628 + }, + { + "variant": "fixed-buffer", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 520, + "afterCompletionDelta": 532 + }, + { + "variant": "fixed-buffer", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 520, + "afterCompletionDelta": 532 + }, + { + "variant": "fixed-buffer", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 17, + "heapDelta": 792, + "afterCompletionDelta": 560 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scan", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 5192, + "afterCompletionDelta": 4180 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 2604, + "afterCompletionDelta": 2360 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scanReplay", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 5176, + "afterCompletionDelta": 7104 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scanReplay", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 1884, + "afterCompletionDelta": 1640 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 2, + "heapDelta": 2744, + "afterCompletionDelta": 2676 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 1652, + "afterCompletionDelta": 1664 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": -3344, + "afterCompletionDelta": -3332 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 17, + "heapDelta": 1884, + "afterCompletionDelta": 1652 + }, + { + "variant": "fixed-fallback", + "kind": "mouse", + "method": "scan", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 8004, + "afterCompletionDelta": 6992 + }, + { + "variant": "fixed-fallback", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 752, + "afterCompletionDelta": 508 + }, + { + "variant": "fixed-fallback", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 2, + "heapDelta": 672, + "afterCompletionDelta": 1464 + }, + { + "variant": "fixed-fallback", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 520, + "afterCompletionDelta": 532 + }, + { + "variant": "fixed-fallback", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 1548, + "afterCompletionDelta": 1560 + }, + { + "variant": "fixed-fallback", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 17, + "heapDelta": 776, + "afterCompletionDelta": 544 + } + ] +} diff --git a/docs/audits/terminal-mode-tail-retention/load-source.cjs b/docs/audits/terminal-mode-tail-retention/load-source.cjs new file mode 100644 index 00000000000..fdc39d2b3c6 --- /dev/null +++ b/docs/audits/terminal-mode-tail-retention/load-source.cjs @@ -0,0 +1,68 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const Module = require('node:module') +const { createHash } = require('node:crypto') +const { build } = require('esbuild') + +const root = path.resolve(__dirname, '../../..') +const sha = (value) => createHash('sha256').update(value).digest('hex') +const read = (file) => fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n') +const versionsText = read(path.join(__dirname, 'source-versions.json')) +const versions = JSON.parse(versionsText) + +async function loadSource(fixed) { + const evaluatedSources = {} + const built = await build({ + stdin: { + contents: [ + "export { TerminalKittyKeyboardModeTracker } from './src/shared/terminal-kitty-keyboard-mode-tracker'", + "export { TerminalMouseModeMirror } from './src/main/daemon/terminal-mouse-mode-mirror'", + "export { ownRetainedString, resetOwnRetainedStringCopier } from './src/shared/own-retained-string'" + ].join('\n'), + resolveDir: root + }, + bundle: true, + platform: 'node', + format: 'cjs', + write: false, + plugins: [ + { + name: 'hash-fenced-retained-mode-tails', + setup(builder) { + builder.onLoad({ filter: /\.ts$/ }, ({ path: filename }) => { + const relative = path.relative(root, filename).split(path.sep).join('/') + const version = versions.sources[relative] + assert.ok(version, `Unreviewed source: ${relative}`) + let contents = read(filename) + assert.equal(sha(contents), version.fixedSha256, `Fixed source changed: ${relative}`) + if (!fixed && version.reverse) { + for (const { from, to, count } of version.reverse) { + assert.equal(contents.split(from).length - 1, count) + contents = contents.replaceAll(from, to) + } + } + const expected = fixed ? version.fixedSha256 : version.baselineSha256 + assert.equal(sha(contents), expected, `Evaluated source changed: ${relative}`) + evaluatedSources[relative] = sha(contents) + return { contents, loader: 'ts' } + }) + } + } + ] + }) + assert.deepEqual(Object.keys(evaluatedSources).sort(), Object.keys(versions.sources).sort()) + const filename = path.join(__dirname, fixed ? 'fixed-bundle.cjs' : 'baseline-bundle.cjs') + const loaded = new Module(filename, module) + loaded.filename = filename + loaded.paths = Module._nodeModulePaths(root) + loaded._compile(built.outputFiles[0].text, filename) + return { + ...loaded.exports, + evaluatedSources, + bundleSha256: sha(built.outputFiles[0].text), + sourceVersionsSha256: sha(versionsText) + } +} + +module.exports = { loadSource, sha, read } diff --git a/docs/audits/terminal-mode-tail-retention/node-results.json b/docs/audits/terminal-mode-tail-retention/node-results.json new file mode 100644 index 00000000000..e43d49c58df --- /dev/null +++ b/docs/audits/terminal-mode-tail-retention/node-results.json @@ -0,0 +1,467 @@ +{ + "node": "v26.6.0", + "electron": null, + "v8": "14.6.202.34-node.26", + "platform": "darwin", + "runnerSha256": "b78dc54a08345732235d4fcc1f72ca1534b00f89280981beda00667d22b8f291", + "loaderSha256": "730da9091abba3997432657aef4a2e6e7a3dd0e9218ea78f1838806b6c0fa789", + "pendingTailCodeUnits": 18, + "clearedRegexStatics": true, + "bundles": { + "baseline": { + "bundleSha256": "6023f5d6868f5db601f6883acda5d56990d204084a6a3c812799aff1e533a5b0", + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/terminal-kitty-keyboard-mode-tracker.ts": "2ee0bad47d4575046f71c16e0334a8ae8be531f0cfc67133abc287f8ae5aa403", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225" + }, + "sourceVersionsSha256": "29cde88cb882b2d7726a2d4a077484217076090acc009f47a805e368f597e51a" + }, + "fixed-buffer": { + "bundleSha256": "bbae10735fc999eea9dafffcd0e1fc4c47e1a8f1daf0045ed074b08cf3a93d45", + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/shared/terminal-kitty-keyboard-mode-tracker.ts": "a8da704564b9ab97aa344f380940021d3fa98f80af817751824d14271acb8a84", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225" + }, + "sourceVersionsSha256": "29cde88cb882b2d7726a2d4a077484217076090acc009f47a805e368f597e51a" + }, + "fixed-fallback": { + "bundleSha256": "bbae10735fc999eea9dafffcd0e1fc4c47e1a8f1daf0045ed074b08cf3a93d45", + "evaluatedSources": { + "src/shared/own-retained-string.ts": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "src/main/daemon/terminal-mouse-mode-mirror.ts": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "src/shared/terminal-kitty-keyboard-mode-tracker.ts": "a8da704564b9ab97aa344f380940021d3fa98f80af817751824d14271acb8a84", + "src/shared/owned-utf16-suffix.ts": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "src/shared/terminal-kitty-keyboard-flags.ts": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225" + }, + "sourceVersionsSha256": "29cde88cb882b2d7726a2d4a077484217076090acc009f47a805e368f597e51a" + } + }, + "reports": [ + { + "variant": "baseline", + "kind": "kitty", + "method": "scan", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 2120952, + "afterCompletionDelta": 31112 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 33557944, + "afterCompletionDelta": 3840 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scanReplay", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 2107000, + "afterCompletionDelta": 9336 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scanReplay", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 33557928, + "afterCompletionDelta": 3752 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 2, + "heapDelta": 3320, + "afterCompletionDelta": 3288 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 3176, + "afterCompletionDelta": 4792 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 3176, + "afterCompletionDelta": 4904 + }, + { + "variant": "baseline", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 17, + "heapDelta": 33557944, + "afterCompletionDelta": 3232 + }, + { + "variant": "baseline", + "kind": "mouse", + "method": "scan", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 2111984, + "afterCompletionDelta": 13904 + }, + { + "variant": "baseline", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 33557072, + "afterCompletionDelta": 2272 + }, + { + "variant": "baseline", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 2, + "heapDelta": 1232, + "afterCompletionDelta": 1232 + }, + { + "variant": "baseline", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 992, + "afterCompletionDelta": 1008 + }, + { + "variant": "baseline", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 992, + "afterCompletionDelta": 1008 + }, + { + "variant": "baseline", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 17, + "heapDelta": 33555840, + "afterCompletionDelta": 1048 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scan", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 18376, + "afterCompletionDelta": 25472 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 3448, + "afterCompletionDelta": 3144 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scanReplay", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 9400, + "afterCompletionDelta": 8216 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scanReplay", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 3696, + "afterCompletionDelta": 3392 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 2, + "heapDelta": 3320, + "afterCompletionDelta": 3240 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 3176, + "afterCompletionDelta": 4648 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 3128, + "afterCompletionDelta": 3144 + }, + { + "variant": "fixed-buffer", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 17, + "heapDelta": 3448, + "afterCompletionDelta": 3152 + }, + { + "variant": "fixed-buffer", + "kind": "mouse", + "method": "scan", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 15112, + "afterCompletionDelta": 14376 + }, + { + "variant": "fixed-buffer", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 1312, + "afterCompletionDelta": 1008 + }, + { + "variant": "fixed-buffer", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 2, + "heapDelta": 1232, + "afterCompletionDelta": 1200 + }, + { + "variant": "fixed-buffer", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 992, + "afterCompletionDelta": 1008 + }, + { + "variant": "fixed-buffer", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 992, + "afterCompletionDelta": 1008 + }, + { + "variant": "fixed-buffer", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 17, + "heapDelta": 1344, + "afterCompletionDelta": 1048 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scan", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 9480, + "afterCompletionDelta": 8216 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 3448, + "afterCompletionDelta": 4048 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scanReplay", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 13216, + "afterCompletionDelta": 11952 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scanReplay", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 3448, + "afterCompletionDelta": 3144 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 2, + "heapDelta": 3320, + "afterCompletionDelta": 3240 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 3176, + "afterCompletionDelta": 3192 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 3912, + "afterCompletionDelta": 3928 + }, + { + "variant": "fixed-fallback", + "kind": "kitty", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 17, + "heapDelta": 3448, + "afterCompletionDelta": 3152 + }, + { + "variant": "fixed-fallback", + "kind": "mouse", + "method": "scan", + "inputChars": 65536, + "count": 32, + "expectedTailLength": 18, + "heapDelta": 9336, + "afterCompletionDelta": 8072 + }, + { + "variant": "fixed-fallback", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 18, + "heapDelta": 1312, + "afterCompletionDelta": 1008 + }, + { + "variant": "fixed-fallback", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 2, + "heapDelta": 1232, + "afterCompletionDelta": 8184 + }, + { + "variant": "fixed-fallback", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 992, + "afterCompletionDelta": 1008 + }, + { + "variant": "fixed-fallback", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 0, + "heapDelta": 2280, + "afterCompletionDelta": 2296 + }, + { + "variant": "fixed-fallback", + "kind": "mouse", + "method": "scan", + "inputChars": 4194304, + "count": 8, + "expectedTailLength": 17, + "heapDelta": 1312, + "afterCompletionDelta": 1016 + } + ] +} diff --git a/docs/audits/terminal-mode-tail-retention/reproduce.cjs b/docs/audits/terminal-mode-tail-retention/reproduce.cjs new file mode 100644 index 00000000000..61801919b7d --- /dev/null +++ b/docs/audits/terminal-mode-tail-retention/reproduce.cjs @@ -0,0 +1,205 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const { loadSource, sha, read } = require('./load-source.cjs') + +assert.equal(process.env.ORCA_BACKGROUND_LAUNCH, '1') +assert.equal(typeof global.gc, 'function', 'Run with --expose-gc') +const pending = '\x1b[?1049;2004;1000;' +const sizes = [ + [64 * 1024, 32], + [4 * 1024 * 1024, 8] +] + +async function heap() { + // Isolate owner storage from V8's process-wide last successful regexp input. + ;/reset/.test('reset') + for (let round = 0; round < 4; round++) { + await new Promise((resolve) => setImmediate(resolve)) + global.gc() + } + return process.memoryUsage().heapUsed +} + +function createOwner(Owner, method, chars, index, suffix) { + const prefix = `${index}:` + const data = `${prefix}${'x'.repeat(chars - prefix.length - suffix.length)}${suffix}` + const owner = new Owner() + owner[method](data) + return owner +} + +async function measure(Owner, variant, kind, method, inputChars, count, suffix = pending) { + const beforeHeap = await heap() + const owners = Array.from({ length: count }, (_, index) => + createOwner(Owner, method, inputChars, index, suffix) + ) + const heapDelta = (await heap()) - beforeHeap + const expectedTailLength = suffix.length > 4096 || suffix.endsWith('h') ? 0 : suffix.length + assert.ok(owners.every((owner) => owner.scanTail.length === expectedTailLength)) + const retainsParent = variant === 'baseline' && expectedTailLength >= 13 + assert.ok( + retainsParent ? heapDelta > inputChars * count * 0.75 : heapDelta < 768 * 1024, + JSON.stringify({ variant, kind, method, inputChars, count, expectedTailLength, heapDelta }) + ) + + for (const owner of owners) { + owner[method]('1006h') + if (kind === 'kitty') { + if (suffix === pending) { + assert.equal(owner.isAlternateScreen, true) + } + owner[method]('\x1b[>3u') + assert.equal(owner.flags, 3) + owner.scan('\x1b[= 13) { + assert.equal(owner.mouseTrackingMode, 'vt200') + assert.equal(owner.sgrMouseMode, true) + } + assert.equal(owner.scanTail, '') + } + const afterCompletionDelta = (await heap()) - beforeHeap + assert.ok( + afterCompletionDelta < 768 * 1024, + JSON.stringify({ variant, kind, method, afterCompletionDelta }) + ) + for (const owner of owners) { + if (kind === 'kitty') { + owner.resetForSnapshot() + assert.equal(owner.snapshotFlags, undefined) + } else { + owner.scan('\x1bc') + assert.equal(owner.mouseTrackingMode, 'none') + assert.equal(owner.sgrMouseMode, false) + } + } + return { + variant, + kind, + method, + inputChars, + count, + expectedTailLength, + heapDelta, + afterCompletionDelta + } +} + +function configureCopier(api, fallback) { + api.resetOwnRetainedStringCopier() + const originalBuffer = globalThis.Buffer + try { + if (fallback) { + globalThis.Buffer = undefined + } + assert.equal(api.ownRetainedString(pending), pending) + } finally { + globalThis.Buffer = originalBuffer + } +} + +function behavior(Tracker, Mirror) { + const replay = new Tracker() + for (let index = 0; index < 70; index++) { + replay.scanReplay('\x1b[>3u') + } + assert.equal(replay.mainStack.length, 0) + assert.equal(replay.flags, 3) + replay.scan('\x1b[3u') + } + assert.equal(live.mainStack.length, 16) + live.scan('\x1b[?1049h\x1b[>5u') + assert.equal(live.altStack.length, 1) + live.scan('\x1b[?1049l') + assert.equal(live.flags, 3) + live.scan(`\x1bc${pending}`) + assert.equal(live.flags, 0) + assert.equal(live.scanTail, pending) + live.scan('1006h') + assert.equal(live.isAlternateScreen, true) + live.reset() + assert.equal(live.scanTail, '') + assert.equal(live.snapshotFlags, 0) + + const mouse = new Mirror() + mouse.scan('\x1b[?1003;1016h') + assert.equal(mouse.mouseTrackingMode, 'any') + assert.equal(mouse.sgrMousePixelsMode, true) + mouse.scan('\x9b?1002;1006h') + assert.equal(mouse.mouseTrackingMode, 'drag') + assert.equal(mouse.sgrMouseMode, true) + assert.equal(mouse.sgrMousePixelsMode, false) + mouse.scan(`\x1bc${pending}`) + assert.equal(mouse.mouseTrackingMode, 'none') + assert.equal(mouse.scanTail, pending) + mouse.scan('1006h') + assert.equal(mouse.mouseTrackingMode, 'vt200') + assert.equal(mouse.sgrMouseMode, true) +} + +async function main() { + const reports = [] + const bundles = {} + for (const variant of ['baseline', 'fixed-buffer', 'fixed-fallback']) { + const api = await loadSource(variant !== 'baseline') + const { TerminalKittyKeyboardModeTracker: Tracker, TerminalMouseModeMirror: Mirror } = api + bundles[variant] = { + bundleSha256: api.bundleSha256, + evaluatedSources: api.evaluatedSources, + sourceVersionsSha256: api.sourceVersionsSha256 + } + configureCopier(api, variant === 'fixed-fallback') + behavior(Tracker, Mirror) + for (const [kind, Owner, methods] of [ + ['kitty', Tracker, ['scan', 'scanReplay']], + ['mouse', Mirror, ['scan']] + ]) { + for (const method of methods) { + for (const [chars, count] of sizes) { + reports.push(await measure(Owner, variant, kind, method, chars, count)) + } + } + for (const suffix of [ + '\x1b[', + '\x1b[?1049;2004;1000;1006h', + `\x1b[${'1'.repeat(4095)}`, + pending.replace('\x1b[', '\x9b') + ]) { + reports.push(await measure(Owner, variant, kind, 'scan', 4 * 1024 * 1024, 8, suffix)) + } + } + } + const report = { + node: process.version, + electron: process.versions.electron ?? null, + v8: process.versions.v8, + platform: process.platform, + runnerSha256: sha(read(__filename)), + loaderSha256: sha(read(path.join(__dirname, 'load-source.cjs'))), + pendingTailCodeUnits: pending.length, + clearedRegexStatics: true, + bundles, + reports + } + const output = path.join( + __dirname, + `${process.versions.electron ? 'electron' : 'node'}-results.json` + ) + fs.writeFileSync(output, `${JSON.stringify(report, null, 2)}\n`) + console.log( + JSON.stringify({ output, passed: reports.length, node: report.node, electron: report.electron }) + ) +} +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) +setTimeout(() => { + console.error('fixture deadline') + process.exit(2) +}, 30000).unref() diff --git a/docs/audits/terminal-mode-tail-retention/source-versions.json b/docs/audits/terminal-mode-tail-retention/source-versions.json new file mode 100644 index 00000000000..fe29f805765 --- /dev/null +++ b/docs/audits/terminal-mode-tail-retention/source-versions.json @@ -0,0 +1,44 @@ +{ + "publicationTopic": "np-oom-scan-retained-text-slices", + "publicationBaseCommit": "8d599520e44654a5c28e9930e3070c00d6499931", + "historicalParserRef": "v1.4.198", + "historicalScope": "Both parser modules and kitty flag parser match this ref; owned-string helpers come from the publication topic. This is not a historical app binary.", + "sources": { + "src/shared/terminal-kitty-keyboard-mode-tracker.ts": { + "baselineSha256": "2ee0bad47d4575046f71c16e0334a8ae8be531f0cfc67133abc287f8ae5aa403", + "fixedSha256": "a8da704564b9ab97aa344f380940021d3fa98f80af817751824d14271acb8a84", + "reverse": [ + { + "from": "import { ownRetainedString } from './own-retained-string'\n", + "to": "", + "count": 1 + }, + { "from": "? ownRetainedString(tail) : ''", "to": "? tail : ''", "count": 1 } + ] + }, + "src/main/daemon/terminal-mouse-mode-mirror.ts": { + "baselineSha256": "5bf8a94ad7ec3fdd151ba7559f49a229b2156818aba88f7b562bef258048acf1", + "fixedSha256": "e8f4888dabee5c4cc50b0f15584b3b1b5e2da4db1ba25695dd5762ef9c4a6fb3", + "reverse": [ + { + "from": "import { ownRetainedString } from '../../shared/own-retained-string'\n", + "to": "", + "count": 1 + }, + { "from": "? ownRetainedString(tail) : ''", "to": "? tail : ''", "count": 2 } + ] + }, + "src/shared/own-retained-string.ts": { + "baselineSha256": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2", + "fixedSha256": "addea88bc11c725192be2ff617b91315a875113844c95de03e5529cf3d7ef2e2" + }, + "src/shared/owned-utf16-suffix.ts": { + "baselineSha256": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475", + "fixedSha256": "1ca0bb35dfff2d9f3c02caee3d05b0e0c97378df71c878622acf703524d2c475" + }, + "src/shared/terminal-kitty-keyboard-flags.ts": { + "baselineSha256": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225", + "fixedSha256": "ee8ca63afdca8356fa12878e74973ff92d807aa8d99750b2415b42f4bccb0225" + } + } +} diff --git a/src/main/claude/claude-background-task-frames.ts b/src/main/claude/claude-background-task-frames.ts index c34d8224cef..7be5b190588 100644 --- a/src/main/claude/claude-background-task-frames.ts +++ b/src/main/claude/claude-background-task-frames.ts @@ -8,6 +8,7 @@ import type { AgentSessionBackgroundTaskRunState } from '../../shared/agent-session-wire' import { backgroundTaskFallbackText } from '../../shared/native-chat-background-task-row' +import { ownRetainedString } from '../../shared/own-retained-string' const MAX_TASK_ID_LENGTH = 512 const MAX_TASK_TEXT_LENGTH = 512 @@ -39,7 +40,7 @@ function boundedTaskText(value: unknown): string | undefined { return undefined } const trimmed = value.trim().replace(/\s+/g, ' ') - return trimmed.length > 0 ? trimmed.slice(0, MAX_TASK_TEXT_LENGTH) : undefined + return trimmed.length > 0 ? ownRetainedString(trimmed.slice(0, MAX_TASK_TEXT_LENGTH)) : undefined } export function taskDescription(value: unknown): string | undefined { diff --git a/src/main/claude/claude-background-task-retention.test.ts b/src/main/claude/claude-background-task-retention.test.ts new file mode 100644 index 00000000000..f5917cb3d45 --- /dev/null +++ b/src/main/claude/claude-background-task-retention.test.ts @@ -0,0 +1,96 @@ +import { setImmediate } from 'node:timers/promises' +import { expect, it } from 'vitest' +import { ClaudeBackgroundTaskTracker } from './claude-background-task-tracker' +import { taskDescription, taskName } from './claude-background-task-frames' + +type Retention = 'live' | 'settled' | 'removed' +type Field = 'description' | 'name' +const TASKS = 8 +const INPUT_CHARS = 1024 * 1024 + +function collectHeap(): number { + const collect = globalThis.gc + if (typeof collect !== 'function') { + throw new Error('global.gc unavailable: run with the repository Vitest --expose-gc config') + } + for (let index = 0; index < 3; index++) { + collect() + } + return process.memoryUsage().heapUsed +} + +function populate(field: Field, retention: Retention, count = TASKS): ClaudeBackgroundTaskTracker { + const tracker = new ClaudeBackgroundTaskTracker(() => 1) + const keeper = { + type: 'system', + subtype: 'task_started', + task_id: 'keeper', + task_type: 'local_bash', + is_backgrounded: true + } + tracker.observe(keeper) + for (let index = 0; index < count; index++) { + tracker.observe( + JSON.parse( + JSON.stringify({ + ...keeper, + task_id: `task-${index}`, + [field]: String.fromCharCode(65 + index).repeat(INPUT_CHARS) + }) + ) + ) + if (retention === 'settled') { + tracker.observe({ + type: 'system', + subtype: 'task_notification', + task_id: `task-${index}`, + status: 'completed' + }) + } + } + if (retention === 'removed') { + tracker.observe({ type: 'system', subtype: 'background_tasks_changed', tasks: [keeper] }) + } + return tracker +} + +it.each([ + ['description', 'live'], + ['description', 'settled'], + ['description', 'removed'], + ['name', 'live'], + ['name', 'settled'], + ['name', 'removed'] +] as const)('owns bounded %s text retained by %s tasks', async (field, retention) => { + populate(field, retention, 1).clear() + await setImmediate() + const before = collectHeap() + const tracker = populate(field, retention) + await setImmediate() + try { + expect(collectHeap() - before).toBeLessThan(2 * 1024 * 1024) + if (retention === 'live') { + expect(tracker.state?.tasks?.find((task) => task.id === 'task-0')?.[field]).toBe( + 'A'.repeat(512) + ) + } else if (retention === 'settled') { + expect(tracker.state?.settledTasks?.find((task) => task.id === 'task-0')?.[field]).toBe( + 'A'.repeat(512) + ) + } else { + expect(tracker.state?.tasks?.map((task) => task.id)).toEqual(['keeper']) + } + } finally { + tracker.clear() + } +}) + +it('preserves normalization, name fallback, and the UTF-16 clipping boundary', () => { + expect(taskDescription(' \t run\n the\r\n build ')).toBe('run the build') + expect(taskDescription(' \t\r\n ')).toBeUndefined() + expect(taskDescription(null)).toBeUndefined() + expect(taskName({ name: ' ', agent_type: '\t reviewer\nagent ' })).toBe('reviewer agent') + const value = `${'漢'.repeat(511)}\ud83d\ude00\udfff` + expect(taskDescription(value)).toBe(value.slice(0, 512)) + expect(taskName({ subagent_type: value })).toBe(value.slice(0, 512)) +}) diff --git a/src/main/daemon/terminal-mouse-mode-mirror.ts b/src/main/daemon/terminal-mouse-mode-mirror.ts index 8f8b284f7f4..7e2ea703cac 100644 --- a/src/main/daemon/terminal-mouse-mode-mirror.ts +++ b/src/main/daemon/terminal-mouse-mode-mirror.ts @@ -1,3 +1,4 @@ +import { ownRetainedString } from '../../shared/own-retained-string' import type { TerminalModes } from './types' type MouseTrackingMode = NonNullable @@ -105,10 +106,10 @@ export class TerminalMouseModeMirror { return tail } if (tail.startsWith('\x1b[?')) { - return this.isIncompleteParams(tail.slice(3)) ? tail : '' + return this.isIncompleteParams(tail.slice(3)) ? ownRetainedString(tail) : '' } if (tail.startsWith('\x9b?')) { - return this.isIncompleteParams(tail.slice(2)) ? tail : '' + return this.isIncompleteParams(tail.slice(2)) ? ownRetainedString(tail) : '' } return '' } diff --git a/src/main/daemon/terminal-mouse-tail-retention.test.ts b/src/main/daemon/terminal-mouse-tail-retention.test.ts new file mode 100644 index 00000000000..7a266b4b23d --- /dev/null +++ b/src/main/daemon/terminal-mouse-tail-retention.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from 'vitest' +import { TerminalMouseModeMirror } from './terminal-mouse-mode-mirror' + +function heapAfterGc(): number { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + // Isolate mirror ownership from V8's process-wide last successful regexp input. + void /reset/.test('reset') + globalThis.gc() + globalThis.gc() + return process.memoryUsage().heapUsed +} + +describe('mouse mode scan tail retention', () => { + it.each(['\x1b[', '\x9b'])( + 'retains a split %j mode sequence without retaining consumed output', + (introducer) => { + const before = heapAfterGc() + const mirrors = Array.from({ length: 8 }, (_value, index) => { + const mirror = new TerminalMouseModeMirror() + mirror.scan(`${index}:${'x'.repeat(4 * 1024 * 1024)}${introducer}?1049;2004;1000;`) + return mirror + }) + + expect(heapAfterGc() - before).toBeLessThan(2 * 1024 * 1024) + for (const mirror of mirrors) { + expect(mirror.mouseTrackingMode).toBe('none') + mirror.scan('1006h') + expect(mirror.mouseTrackingMode).toBe('vt200') + expect(mirror.sgrMouseMode).toBe(true) + mirror.scan('\x1b[?1016h') + expect(mirror.sgrMouseMode).toBe(false) + expect(mirror.sgrMousePixelsMode).toBe(true) + mirror.scan('\x1bc') + expect(mirror.mouseTrackingMode).toBe('none') + expect(mirror.sgrMousePixelsMode).toBe(false) + } + } + ) +}) diff --git a/src/main/plugins/plugin-worker-output-buffer.ts b/src/main/plugins/plugin-worker-output-buffer.ts index 836330c8799..6a0cb2a078b 100644 --- a/src/main/plugins/plugin-worker-output-buffer.ts +++ b/src/main/plugins/plugin-worker-output-buffer.ts @@ -1,4 +1,5 @@ import type { Readable } from 'node:stream' +import { ownRetainedString } from '../../shared/own-retained-string' type PluginWorkerOutputSink = (level: 'info' | 'warn' | 'error', line: string) => void @@ -21,9 +22,11 @@ export function pipePluginWorkerOutput( if (line.trim().length > 0) { log( level, - truncated - ? `${line.slice(0, PLUGIN_WORKER_OUTPUT_LINE_LIMIT - TRUNCATION_SUFFIX.length)}${TRUNCATION_SUFFIX}` - : line + ownRetainedString( + truncated + ? `${line.slice(0, PLUGIN_WORKER_OUTPUT_LINE_LIMIT - TRUNCATION_SUFFIX.length)}${TRUNCATION_SUFFIX}` + : line + ) ) } } @@ -49,7 +52,7 @@ export function pipePluginWorkerOutput( buffered = '' discarding = newline === -1 } else { - buffered += segment + buffered += newline === -1 ? ownRetainedString(segment) : segment if (newline !== -1) { emit(buffered) buffered = '' diff --git a/src/main/plugins/plugin-worker-output-retention.test.ts b/src/main/plugins/plugin-worker-output-retention.test.ts new file mode 100644 index 00000000000..35784541f74 --- /dev/null +++ b/src/main/plugins/plugin-worker-output-retention.test.ts @@ -0,0 +1,82 @@ +import { once } from 'node:events' +import { PassThrough } from 'node:stream' +import { describe, expect, it } from 'vitest' +import { PluginLogBuffer } from './plugin-log-buffer' +import { pipePluginWorkerOutput } from './plugin-worker-output-buffer' + +async function heapAfterGc(): Promise { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + for (let round = 0; round < 3; round++) { + await new Promise((resolve) => setImmediate(resolve)) + globalThis.gc() + } + return process.memoryUsage().heapUsed +} + +async function endStream(stream: PassThrough): Promise { + const ended = once(stream, 'end') + stream.end() + await ended +} + +function writeTail(stream: PassThrough, index: number): void { + stream.write(`${' '.repeat(4 * 1024 * 1024)}\nretained output ${index}`) +} + +function writeLine(stream: PassThrough, index: number, truncated: boolean): void { + const prefix = String(index).padStart(4, '0') + stream.write( + truncated + ? `${prefix}${'x'.repeat(64 * 1024)}\n` + : `${' '.repeat(64 * 1024)}\nretained output ${prefix}\n` + ) +} + +describe('plugin worker retained output', () => { + it('keeps unfinished output after consuming a large chunk without retaining the parent', async () => { + const lines: string[] = [] + const before = await heapAfterGc() + const streams = Array.from({ length: 8 }, (_value, index) => { + const stream = new PassThrough() + pipePluginWorkerOutput(stream, 'info', (_level, line) => lines.push(line)) + writeTail(stream, index) + return stream + }) + + expect((await heapAfterGc()) - before).toBeLessThan(2 * 1024 * 1024) + expect(lines).toEqual([]) + for (const stream of streams) { + await endStream(stream) + } + expect(lines).toEqual(Array.from({ length: 8 }, (_value, index) => `retained output ${index}`)) + }) + + it.each([false, true])( + 'owns emitted log text without retaining consumed chunks (truncated=%s)', + async (truncated) => { + const logs = new PluginLogBuffer() + const stream = new PassThrough() + pipePluginWorkerOutput(stream, 'error', (level, line) => logs.append('plugin', level, line)) + const before = await heapAfterGc() + for (let index = 0; index < 205; index++) { + writeLine(stream, index, truncated) + } + await endStream(stream) + + // Compare text after the heap check: comparisons can flatten concatenated strings. + expect((await heapAfterGc()) - before).toBeLessThan(5 * 1024 * 1024) + expect(logs.get('plugin')).toHaveLength(200) + for (const [index, row] of logs.get('plugin').entries()) { + const prefix = String(index + 5).padStart(4, '0') + expect(row.level).toBe('error') + expect(row.line).toBe( + truncated + ? `${prefix}${'x'.repeat(8192 - 4 - '… [truncated]'.length)}… [truncated]` + : `retained output ${prefix}` + ) + } + } + ) +}) diff --git a/src/main/ports/advertised-url-parsing.ts b/src/main/ports/advertised-url-parsing.ts index 1d342e8e03b..f50a1ff253f 100644 --- a/src/main/ports/advertised-url-parsing.ts +++ b/src/main/ports/advertised-url-parsing.ts @@ -1,4 +1,5 @@ /* eslint-disable no-control-regex -- Terminal control-sequence parsing intentionally matches raw control bytes. */ +import { ownRetainedString } from '../../shared/own-retained-string' import type { AdvertisedUrl, AdvertisedUrlChangeEvent, @@ -45,7 +46,7 @@ export class PtyBuffer { const chunkHasLineBreak = chunk.includes('\n') || chunk.includes('\r') // Keep the suffix directly so oversized chunks never materialize a throwaway full concatenation. if (chunk.length >= PER_PTY_BUFFER_LIMIT) { - this.raw = chunk.slice(-PER_PTY_BUFFER_LIMIT) + this.raw = ownRetainedString(chunk.slice(-PER_PTY_BUFFER_LIMIT)) } else if (this.raw.length + chunk.length > PER_PTY_BUFFER_LIMIT) { this.raw = `${this.raw.slice(-(PER_PTY_BUFFER_LIMIT - chunk.length))}${chunk}` } else { diff --git a/src/main/ports/advertised-url-retention.test.ts b/src/main/ports/advertised-url-retention.test.ts new file mode 100644 index 00000000000..9b5cf25b5cf --- /dev/null +++ b/src/main/ports/advertised-url-retention.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from 'vitest' +import { AdvertisedUrlWatcher } from './advertised-url-watcher' + +function heapAfterGc(): number { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + globalThis.gc() + globalThis.gc() + return process.memoryUsage().heapUsed +} + +function ingestOversizedOutput(watcher: AdvertisedUrlWatcher, bound: boolean): void { + for (let index = 0; index < 8; index++) { + const ptyId = `pty-${index}` + if (bound) { + watcher.bindPty(ptyId, 'workspace') + } + watcher.ingest( + ptyId, + `${index}:${'x'.repeat(4 * 1024 * 1024)}\nhttp://localhost:${4100 + index}` + ) + } +} + +describe('advertised URL output retention', () => { + it.each([true, false])('releases oversized parents with PTYs bound=%s', (bound) => { + const watcher = new AdvertisedUrlWatcher() + const before = heapAfterGc() + ingestOversizedOutput(watcher, bound) + expect(heapAfterGc() - before).toBeLessThan(2 * 1024 * 1024) + + for (let index = 0; index < 8; index++) { + const ptyId = `pty-${index}` + watcher.bindPty(ptyId, 'workspace') + watcher.ingest(ptyId, '/\n') + expect(watcher.lookup('workspace', 4100 + index)?.origin).toBe( + `http://localhost:${4100 + index}` + ) + watcher.unbindPty(ptyId) + expect(watcher.lookup('workspace', 4100 + index)).toBeUndefined() + } + }) +}) diff --git a/src/main/ports/advertised-url-watcher.ts b/src/main/ports/advertised-url-watcher.ts index ebbbecdf25f..c26c3e2b9ce 100644 --- a/src/main/ports/advertised-url-watcher.ts +++ b/src/main/ports/advertised-url-watcher.ts @@ -20,6 +20,7 @@ import { lookupBestAdvertisedUrl, shouldEvictAdvertisedUrlAfterScan } from './advertised-url-reconciliation' +import { ownRetainedString } from '../../shared/own-retained-string' export type HostKind = 'custom' | 'loopback' | 'private-ip' | 'public-ip' export type AdvertisedUrl = { @@ -140,7 +141,11 @@ export class AdvertisedUrlWatcher { if (!worktreeId) { // Why: daemon PTY data can arrive before the spawn handler resolves the worktreeId (src/main/ipc/pty.ts:1318-1323); buffer until bindPty replays. const prior = this.pending.get(ptyId) ?? '' - const merged = (prior + chunk).slice(-PENDING_PRE_BIND_LIMIT) + const combined = prior + chunk + const merged = + combined.length > PENDING_PRE_BIND_LIMIT + ? ownRetainedString(combined.slice(-PENDING_PRE_BIND_LIMIT)) + : combined // Why: drop+reinsert refreshes Map insertion order (LRU) so the eviction below drops the oldest unbound PTY. this.pending.delete(ptyId) this.pending.set(ptyId, merged) diff --git a/src/main/runtime/recent-pty-output-buffer.ts b/src/main/runtime/recent-pty-output-buffer.ts index dda01e0afdf..7c067913a93 100644 --- a/src/main/runtime/recent-pty-output-buffer.ts +++ b/src/main/runtime/recent-pty-output-buffer.ts @@ -1,3 +1,5 @@ +import { ownRetainedString } from '../../shared/own-retained-string' + export const RECENT_PTY_OUTPUT_LIMIT = 64 * 1024 // Compact the backing array once this many fully-dropped head slots accumulate, @@ -42,7 +44,7 @@ export class RecentPtyOutputBuffer { return } if (data.length >= this.limit) { - this.chunks = [data.slice(-this.limit)] + this.chunks = [data.length > this.limit ? ownRetainedString(data.slice(-this.limit)) : data] this.headIndex = 0 this.headOffset = 0 this.totalLen = this.limit diff --git a/src/main/runtime/recent-pty-output-retention.test.ts b/src/main/runtime/recent-pty-output-retention.test.ts new file mode 100644 index 00000000000..b5207836e6c --- /dev/null +++ b/src/main/runtime/recent-pty-output-retention.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest' +import { RECENT_PTY_OUTPUT_LIMIT, RecentPtyOutputBuffer } from './recent-pty-output-buffer' + +function heapAfterGc(): number { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + globalThis.gc() + globalThis.gc() + return process.memoryUsage().heapUsed +} + +describe('recent PTY output retention', () => { + it.each([true, false])( + 'releases oversized parent strings with boundary preservation=%s', + (preserveChunkBoundaries) => { + const count = 8 + const before = heapAfterGc() + const buffers = Array.from({ length: count }, (_value, index) => { + const buffer = new RecentPtyOutputBuffer({ preserveChunkBoundaries }) + buffer.append(`${index}:${'x'.repeat(4 * 1024 * 1024)}`) + return buffer + }) + const growth = heapAfterGc() - before + + expect(growth).toBeLessThan(count * RECENT_PTY_OUTPUT_LIMIT * 4) + for (const buffer of buffers) { + expect(buffer.read()).toBe('x'.repeat(RECENT_PTY_OUTPUT_LIMIT)) + expect(buffer.retainedChunks().headChunkIsPartial).toBe(true) + buffer.append('next') + expect(buffer.read()).toBe(`${'x'.repeat(RECENT_PTY_OUTPUT_LIMIT - 4)}next`) + } + } + ) +}) diff --git a/src/renderer/src/components/terminal-pane/deferred-reattach-live-data-queue.ts b/src/renderer/src/components/terminal-pane/deferred-reattach-live-data-queue.ts index 3045569b1e1..86c2f6fe4fd 100644 --- a/src/renderer/src/components/terminal-pane/deferred-reattach-live-data-queue.ts +++ b/src/renderer/src/components/terminal-pane/deferred-reattach-live-data-queue.ts @@ -1,4 +1,5 @@ import type { PtyDataMeta } from './pty-dispatcher' +import { flattenRetainedSlice } from '../../lib/flatten-retained-slice' export const MAX_DEFERRED_REATTACH_LIVE_CHARS = 512 * 1024 export const MAX_DEFERRED_REATTACH_LIVE_CHUNKS = 1_024 @@ -35,7 +36,9 @@ export class DeferredReattachLiveDataQueue { const oversized = chunk.data.length > MAX_DEFERRED_REATTACH_LIVE_CHARS const queuedChunk = { ...chunk, - data: oversized ? chunk.data.slice(-MAX_DEFERRED_REATTACH_LIVE_CHARS) : chunk.data + data: oversized + ? flattenRetainedSlice(chunk.data.slice(-MAX_DEFERRED_REATTACH_LIVE_CHARS)) + : chunk.data } this.chunks.push(queuedChunk) this.retainedChars += queuedChunk.data.length diff --git a/src/renderer/src/components/terminal-pane/pty-eager-buffer-clamp.ts b/src/renderer/src/components/terminal-pane/pty-eager-buffer-clamp.ts index 0d4087e8288..90a8e122303 100644 --- a/src/renderer/src/components/terminal-pane/pty-eager-buffer-clamp.ts +++ b/src/renderer/src/components/terminal-pane/pty-eager-buffer-clamp.ts @@ -1,4 +1,5 @@ import { clampUtf8TextTail } from '../../../../shared/utf8-byte-limits' +import { flattenRetainedSlice } from '../../lib/flatten-retained-slice' export type EagerBufferChunk = { data: string @@ -7,5 +8,8 @@ export type EagerBufferChunk = { export function clampUtf8Tail(data: string, maxBytes: number): EagerBufferChunk { const tail = clampUtf8TextTail(data, maxBytes) - return { data: tail.text, bytes: tail.bytes } + return { + data: tail.text.length < data.length ? flattenRetainedSlice(tail.text) : tail.text, + bytes: tail.bytes + } } diff --git a/src/renderer/src/components/terminal-pane/terminal-capped-buffer-retention.test.ts b/src/renderer/src/components/terminal-pane/terminal-capped-buffer-retention.test.ts new file mode 100644 index 00000000000..5d16aeb6979 --- /dev/null +++ b/src/renderer/src/components/terminal-pane/terminal-capped-buffer-retention.test.ts @@ -0,0 +1,92 @@ +import { describe, expect, it } from 'vitest' +import { capTerminalScrollbackSessionBuffer } from '../../../../shared/workspace-session-terminal-buffers' +import { TERMINAL_SCROLLBACK_SESSION_BUFFER_BYTE_LIMIT } from '../../../../shared/terminal-scrollback-limits' +import { clampUtf8Tail } from './pty-eager-buffer-clamp' +import { PtyShutdownOutputQueue } from './pty-shutdown-output-queue' +import { DeferredReattachLiveDataQueue } from './deferred-reattach-live-data-queue' +import { appendPaneTerminalError, type TerminalErrorsByPaneId } from './terminal-error-accumulation' + +const LIMIT = TERMINAL_SCROLLBACK_SESSION_BUFFER_BYTE_LIMIT +const PARENT_CHARS = 4 * 1024 * 1024 +const COUNT = 8 + +function heapAfterGc(): number { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + globalThis.gc() + globalThis.gc() + return process.memoryUsage().heapUsed +} + +function createPaneErrors(): TerminalErrorsByPaneId { + let errors: TerminalErrorsByPaneId = {} + for (let index = 0; index < COUNT; index++) { + errors = appendPaneTerminalError(errors, 0, `${'x'.repeat(PARENT_CHARS)}:${index}`) + } + return errors +} + +describe('capped terminal buffer retention', () => { + it.each([ + ['persisted scrollback', capTerminalScrollbackSessionBuffer], + ['eager/pre-handler output', (text: string) => clampUtf8Tail(text, LIMIT).data] + ] as const)('detaches %s from oversized incoming strings', (_label, cap) => { + const before = heapAfterGc() + const retained = Array.from({ length: COUNT }, (_value, index) => + cap(`${index}:${'x'.repeat(PARENT_CHARS)}`) + ) + const growth = heapAfterGc() - before + + expect(retained.every((text) => text === 'x'.repeat(LIMIT))).toBe(true) + expect(growth).toBeLessThan(COUNT * LIMIT * 2) + }) + + it('keeps shutdown queue heap storage near its byte ledger after clamping', () => { + const before = heapAfterGc() + const queues = Array.from({ length: COUNT }, (_value, index) => { + const queue = new PtyShutdownOutputQueue() + queue.enqueue({ kind: 'replay', data: `${index}:${'x'.repeat(PARENT_CHARS)}` }) + return queue + }) + const growth = heapAfterGc() - before + + expect(queues.every((queue) => queue.getStorageForTest().retainedBytes === LIMIT)).toBe(true) + expect(growth).toBeLessThan(COUNT * LIMIT * 2) + for (const queue of queues) { + expect(queue.takeAll()).toEqual([{ kind: 'replay', data: 'x'.repeat(LIMIT) }]) + } + }) + + it('detaches oversized chunks while a reattach queue waits for its consumer', () => { + const before = heapAfterGc() + const queues = Array.from({ length: COUNT }, (_value, index) => { + const queue = new DeferredReattachLiveDataQueue() + queue.enqueue({ + data: `${index}:${'x'.repeat(PARENT_CHARS)}`, + ptyId: 'p', + streamGeneration: 1 + }) + return queue + }) + const growth = heapAfterGc() - before + + expect(queues.every((queue) => queue.getStorageForTest().retainedChars === LIMIT)).toBe(true) + expect(growth).toBeLessThan(COUNT * LIMIT * 2) + for (const queue of queues) { + expect(queue.takeAll()[0]?.data).toBe('x'.repeat(LIMIT)) + } + }) + + it('keeps capped pane errors without retaining the original error payloads', () => { + const before = heapAfterGc() + const errors = createPaneErrors() + const growth = heapAfterGc() - before + + expect(errors[0]).toHaveLength(COUNT) + expect( + errors[0].every((text, index) => text.length === 4000 && text.endsWith(`:${index}`)) + ).toBe(true) + expect(growth).toBeLessThan(PARENT_CHARS) + }) +}) diff --git a/src/renderer/src/components/terminal-pane/terminal-error-accumulation.ts b/src/renderer/src/components/terminal-pane/terminal-error-accumulation.ts index 63c5de8423e..d22364e74ad 100644 --- a/src/renderer/src/components/terminal-pane/terminal-error-accumulation.ts +++ b/src/renderer/src/components/terminal-pane/terminal-error-accumulation.ts @@ -1,3 +1,5 @@ +import { flattenRetainedSlice } from '../../lib/flatten-retained-slice' + // The toast still consumes newline-joined copy, so legacy tab-wide messages need // whole-run dedup even though pane errors remain structurally separate until render. function containsWholeLineRun(accumulated: string, message: string): boolean { @@ -22,12 +24,12 @@ export function boundTerminalErrorSurface( const lines = surface.split('\n') let bounded = lines.length > maxLines ? lines.slice(-maxLines).join('\n') : surface if (bounded.length <= maxChars) { - return bounded + return flattenRetainedSlice(bounded) } const suffix = bounded.slice(-maxChars) const firstNewline = suffix.indexOf('\n') bounded = firstNewline === -1 ? suffix : suffix.slice(firstNewline + 1) || suffix - return bounded + return flattenRetainedSlice(bounded) } export function appendPaneTerminalError( diff --git a/src/shared/check-job-log-retention.test.ts b/src/shared/check-job-log-retention.test.ts new file mode 100644 index 00000000000..a1ca9505140 --- /dev/null +++ b/src/shared/check-job-log-retention.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from 'vitest' +import { PR_CHECK_LOG_TAIL_BYTES, sliceCheckLogTail } from './check-job-log-tail-slice' +import { gitLabJobTraceToLogExcerpt } from './gitlab-job-log-excerpt' + +function heapAfterGc(): number { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + globalThis.gc() + globalThis.gc() + return process.memoryUsage().heapUsed +} + +const PARENT_CHARS = 2 * 1024 * 1024 +const COUNT = 8 + +describe('retained CI log excerpts', () => { + it.each([ + [ + 'GitHub long line', + (index: number) => `${index}:${'x'.repeat(PARENT_CHARS)}`, + sliceCheckLogTail + ], + [ + 'GitHub earlier error', + (index: number) => `error: ${index}:${'界'.repeat(PARENT_CHARS)}\n${'recent\n'.repeat(100)}`, + sliceCheckLogTail + ], + [ + 'GitLab raw trace', + (index: number) => `${index}:${'x'.repeat(PARENT_CHARS)}`, + gitLabJobTraceToLogExcerpt + ] + ] as const)('releases the parent of a %s', (_label, makeLog, excerpt) => { + const before = heapAfterGc() + const retained = Array.from({ length: COUNT }, (_value, index) => excerpt(makeLog(index))) + // V8's legacy RegExp statics can otherwise keep the final input independently of our cache. + void /probe/.test('probe') + const growth = heapAfterGc() - before + + expect(retained).toHaveLength(COUNT) + expect(retained.every((text) => Buffer.byteLength(text) <= PR_CHECK_LOG_TAIL_BYTES)).toBe(true) + expect(growth).toBeLessThan(PARENT_CHARS * 2) + }) +}) diff --git a/src/shared/check-job-log-tail-slice.ts b/src/shared/check-job-log-tail-slice.ts index 19afcdfc75d..17b03190875 100644 --- a/src/shared/check-job-log-tail-slice.ts +++ b/src/shared/check-job-log-tail-slice.ts @@ -3,6 +3,7 @@ import { getUtf8ByteLength, isUtf8ByteLengthWithinLimit } from './utf8-byte-limits' +import { ownRetainedString } from './own-retained-string' export const PR_CHECK_LOG_TAIL_LINES = 200 export const PR_CHECK_LOG_TAIL_RECENT_LINES = 100 @@ -57,7 +58,7 @@ function collectEarlierErrorLineIndexes(lines: string[], recentStart: number): n return [...indexes].sort((left, right) => left - right) } -export function sliceCheckLogTail(logText: string): string { +function buildCheckLogTail(logText: string): string { const lines = logText.split(/\r?\n/) const recentStart = Math.max(0, lines.length - PR_CHECK_LOG_TAIL_RECENT_LINES) const recentLines = lines.slice(recentStart) @@ -80,3 +81,8 @@ export function sliceCheckLogTail(logText: string): string { recentLines ) } + +export function sliceCheckLogTail(logText: string): string { + // Cached excerpts must not pin the downloaded log behind a small V8 slice. + return ownRetainedString(buildCheckLogTail(logText)) +} diff --git a/src/shared/command-code-output-retention.test.ts b/src/shared/command-code-output-retention.test.ts new file mode 100644 index 00000000000..681d12270e5 --- /dev/null +++ b/src/shared/command-code-output-retention.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import { createCommandCodeOutputStatusDetector } from './command-code-output-status' + +function heapAfterGc(): number { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + globalThis.gc() + globalThis.gc() + return process.memoryUsage().heapUsed +} + +describe('Command Code output retention', () => { + it('keeps small boundary carries without pinning oversized output on ordinary panes', () => { + const before = heapAfterGc() + const detectors = Array.from({ length: 8 }, (_value, index) => { + const detector = createCommandCodeOutputStatusDetector({ onWorking: () => {} }) + detector.observe(`${index}:${'x'.repeat(4 * 1024 * 1024)}`) + return detector + }) + expect(heapAfterGc() - before).toBeLessThan(2 * 1024 * 1024) + for (const detector of detectors) { + expect(detector.observe('\nordinary shell output\n')).toBe(false) + } + }) +}) diff --git a/src/shared/command-code-output-status.ts b/src/shared/command-code-output-status.ts index e98f72d857b..b5fb611a73d 100644 --- a/src/shared/command-code-output-status.ts +++ b/src/shared/command-code-output-status.ts @@ -11,6 +11,7 @@ import { } from './command-code-prompt-text' import { stripTerminalControl } from './terminal-control-stripping' import { escapeRegex } from './string-utils' +import { ownRetainedString } from './own-retained-string' export { stripTerminalControl } from './terminal-control-stripping' @@ -159,7 +160,7 @@ function rawChunkMayContainCommandCodeBanner(previousRawText: string, data: stri function appendRecentRawText(previousRawText: string, data: string): string { if (data.length >= RECENT_TEXT_LIMIT) { - return data.slice(-RECENT_TEXT_LIMIT) + return ownRetainedString(data.slice(-RECENT_TEXT_LIMIT)) } return (previousRawText + data).slice(-RECENT_TEXT_LIMIT) } diff --git a/src/shared/terminal-kitty-keyboard-mode-tracker.ts b/src/shared/terminal-kitty-keyboard-mode-tracker.ts index cae87067973..72c9d5b91f3 100644 --- a/src/shared/terminal-kitty-keyboard-mode-tracker.ts +++ b/src/shared/terminal-kitty-keyboard-mode-tracker.ts @@ -1,3 +1,4 @@ +import { ownRetainedString } from './own-retained-string' import { parseTerminalKittyKeyboardFlags } from './terminal-kitty-keyboard-flags' // Why: PTY/SSH chunks can split an escape sequence before its final byte. @@ -308,7 +309,7 @@ export class TerminalKittyKeyboardModeTracker { if (body === null) { return '' } - return this.isIncompleteSequenceBody(body) ? tail : '' + return this.isIncompleteSequenceBody(body) ? ownRetainedString(tail) : '' } private isIncompleteSequenceBody(body: string): boolean { diff --git a/src/shared/terminal-kitty-keyboard-tail-retention.test.ts b/src/shared/terminal-kitty-keyboard-tail-retention.test.ts new file mode 100644 index 00000000000..4c1b92acffd --- /dev/null +++ b/src/shared/terminal-kitty-keyboard-tail-retention.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from 'vitest' +import { TerminalKittyKeyboardModeTracker } from './terminal-kitty-keyboard-mode-tracker' + +const INCOMPLETE_MODE = '\x1b[?1049;2004;1000;' + +function heapAfterGc(): number { + if (!('gc' in globalThis) || typeof globalThis.gc !== 'function') { + throw new Error('The test runner must enable --expose-gc') + } + // Isolate tracker ownership from V8's process-wide last successful regexp input. + void /reset/.test('reset') + globalThis.gc() + globalThis.gc() + return process.memoryUsage().heapUsed +} + +describe('kitty keyboard scan tail retention', () => { + it.each(['scan', 'scanReplay'] as const)( + '%s retains a split mode sequence without retaining consumed output', + (method) => { + const before = heapAfterGc() + const trackers = Array.from({ length: 8 }, (_value, index) => { + const tracker = new TerminalKittyKeyboardModeTracker() + tracker[method](`${index}:${'x'.repeat(4 * 1024 * 1024)}${INCOMPLETE_MODE}`) + return tracker + }) + + expect(heapAfterGc() - before).toBeLessThan(2 * 1024 * 1024) + for (const tracker of trackers) { + expect(tracker.isAlternateScreen).toBe(false) + tracker[method]('1006h\x1b[>3u') + expect(tracker.isAlternateScreen).toBe(true) + expect(tracker.flags).toBe(3) + tracker.scan('\x1b[ { + vi.unstubAllGlobals() + resetOwnRetainedStringCopier() +}) + +describe.each([false, true])('OSC 133 carry with Bufferless copying=%s', (withoutBuffer) => { + // Syntax from the captured fish 4.7.1 fixture in terminal-mode-2031-final-state.test.ts. + it.each([FISH_PROMPT, FISH_COMMAND])('owns a retained fish suffix %j', (suffix) => { + selectCopier(withoutBuffer) + const started = vi.fn() + const finished = vi.fn() + const before = heapAfterGc() + const scanners = Array.from({ length: 8 }, (_value, index) => { + const scanner = createOsc133CommandFinishedScanner(finished, started) + scanner.scan(`${index}:${'x'.repeat(4 * 1024 * 1024)}${suffix}`) + return scanner + }) + + expect(heapAfterGc() - before).toBeLessThan(2 * 1024 * 1024) + expect(started).not.toHaveBeenCalled() + expect(finished).not.toHaveBeenCalled() + for (const scanner of scanners) { + scanner.scan('\x07\x1b]133;D;137\x1b\\') + } + expect(started).toHaveBeenCalledTimes(suffix === FISH_COMMAND ? scanners.length : 0) + expect(finished.mock.calls).toEqual(Array.from({ length: scanners.length }, () => [137])) + expect(heapAfterGc() - before).toBeLessThan(2 * 1024 * 1024) + }) + + it('preserves UTF-16 carry at every split and retires a reset prefix', () => { + selectCopier(withoutBuffer) + for (let cut = 1; cut < UTF16_CARRY.length; cut += 1) { + const finished = vi.fn() + const scanner = createOsc133CommandFinishedScanner(finished) + scanner.scan(UTF16_CARRY.slice(0, cut)) + scanner.scan(`${UTF16_CARRY.slice(cut)}\x1b\\`) + expect(finished.mock.calls).toEqual([[1234567890]]) + scanner.scan(FISH_COMMAND) + scanner.reset() + scanner.scan('\x07') + expect(finished.mock.calls).toEqual([[1234567890]]) + } + }) +}) + +it('short command-finished carry does not retain consumed output and completes once', () => { + const finished = vi.fn() + const before = heapAfterGc() + const scanners = Array.from({ length: 8 }, (_value, index) => { + const scanner = createOsc133CommandFinishedScanner(finished) + scanner.scan(`${index}:${'x'.repeat(4 * 1024 * 1024)}\x1b]133;D;0`) + return scanner + }) + expect(heapAfterGc() - before).toBeLessThan(2 * 1024 * 1024) + for (const scanner of scanners) { + scanner.scan('\x07') + scanner.scan('\x07') + } + expect(finished.mock.calls).toEqual(Array.from({ length: scanners.length }, () => [0])) +}) + +it('reset releases a pending parent before its terminator arrives', () => { + const finished = vi.fn() + const started = vi.fn() + const before = heapAfterGc() + const scanners = Array.from({ length: 8 }, (_value, index) => { + const scanner = createOsc133CommandFinishedScanner(finished, started) + scanner.scan(`${index}:${'x'.repeat(4 * 1024 * 1024)}${FISH_COMMAND}`) + scanner.reset() + return scanner + }) + expect(heapAfterGc() - before).toBeLessThan(2 * 1024 * 1024) + for (const scanner of scanners) { + scanner.scan('\x07') + } + expect(started).not.toHaveBeenCalled() + expect(finished).not.toHaveBeenCalled() +}) diff --git a/src/shared/terminal-osc133-command-finished.ts b/src/shared/terminal-osc133-command-finished.ts index e4b9cdb8129..9468e1aaa5f 100644 --- a/src/shared/terminal-osc133-command-finished.ts +++ b/src/shared/terminal-osc133-command-finished.ts @@ -8,6 +8,8 @@ * terminators, best-effort exit codes) must be identical in both. */ +import { ownRetainedString } from './own-retained-string' + type OscTerminator = { index: number length: number @@ -91,6 +93,7 @@ export function createOsc133CommandFinishedScanner( if (carry.length > MAX_OSC_CARRY_LENGTH) { carry = carry.slice(carry.length - MAX_OSC_CARRY_LENGTH) } + carry = ownRetainedString(carry) return } diff --git a/src/shared/workspace-session-terminal-buffers.ts b/src/shared/workspace-session-terminal-buffers.ts index 706dbedc2d7..cf971d0d7ab 100644 --- a/src/shared/workspace-session-terminal-buffers.ts +++ b/src/shared/workspace-session-terminal-buffers.ts @@ -5,6 +5,7 @@ import { getRepoIdFromWorktreeId } from './worktree/id' import { TERMINAL_SCROLLBACK_SESSION_BUFFER_BYTE_LIMIT } from './terminal-scrollback-limits' import { clampUtf8TextTail, isUtf8ByteLengthWithinLimit } from './utf8-byte-limits' import { parseExecutionHostId } from './execution-host' +import { ownRetainedString } from './own-retained-string' export type RepoConnection = Pick @@ -53,7 +54,9 @@ export function capTerminalScrollbackSessionBuffer(buffer: string): string { if (isUtf8ByteLengthWithinLimit(buffer, TERMINAL_SCROLLBACK_SESSION_BUFFER_BYTE_LIMIT)) { return buffer } - return clampUtf8TextTail(buffer, TERMINAL_SCROLLBACK_SESSION_BUFFER_BYTE_LIMIT).text + return ownRetainedString( + clampUtf8TextTail(buffer, TERMINAL_SCROLLBACK_SESSION_BUFFER_BYTE_LIMIT).text + ) } function capTerminalScrollbackLeafBuffers(buffers: Record | undefined): { From 28c32f358736b6b01d28f0277203eef7e79f0b69 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 17 Sep 2026 20:35:43 -0700 Subject: [PATCH 076/168] fix(stats): bound retained events during stalled writes (#20941) * fix(stats): cap retained events before asynchronous persistence * test: use typed access in memory retention regressions --------- Co-authored-by: m4air Co-authored-by: m4air --- src/main/stats/collector-async-save.test.ts | 40 +++++++++++++++++++++ src/main/stats/collector.ts | 4 +++ 2 files changed, 44 insertions(+) diff --git a/src/main/stats/collector-async-save.test.ts b/src/main/stats/collector-async-save.test.ts index 80d6c8b5497..5497c1d0d26 100644 --- a/src/main/stats/collector-async-save.test.ts +++ b/src/main/stats/collector-async-save.test.ts @@ -131,6 +131,46 @@ describe('StatsCollector async debounced save', () => { expect(JSON.parse(readFileSync(statsPath(), 'utf-8')).aggregates.totalAgentsSpawned).toBe(5) }) + it('bounds retained events while a stalled write prevents serialization', async () => { + vi.useFakeTimers() + const { StatsCollector, initStatsPath } = await importCollector() + initStatsPath() + const collector = new StatsCollector() + + gate.blocked = true + collector.record({ type: 'agent_start', at: 0 }) + await vi.advanceTimersByTimeAsync(5_000) + await vi.waitFor(() => expect(gate.writeFileCalls).toBe(1)) + + const expectedEvents = Array.from({ length: 10_000 }, (_, index) => ({ + type: 'agent_start', + at: index + 10_001 + })) + try { + for (let at = 1; at <= 20_000; at += 1) { + collector.record({ type: 'agent_start', at }) + if (at % 5_000 === 0) { + await vi.advanceTimersByTimeAsync(5_000) + expect(collector['events'].length).toBeLessThanOrEqual(10_000) + } + } + expect(gate.writeFileCalls).toBe(1) + expect(collector['events']).toEqual(expectedEvents) + } finally { + const flushed = collector.flushAsync() + gate.blocked = false + gate.waiters.splice(0).forEach((resolve) => resolve()) + await flushed + } + + const persisted = JSON.parse(readFileSync(statsPath(), 'utf-8')) + expect(persisted.events).toEqual(expectedEvents) + expect(persisted.aggregates).toMatchObject({ + totalAgentsSpawned: 20_001, + firstEventAt: 0 + }) + }) + it('retries a queued final snapshot after the active write fails', async () => { const { StatsCollector, initStatsPath } = await importCollector() initStatsPath() diff --git a/src/main/stats/collector.ts b/src/main/stats/collector.ts index ba484d63a57..9fd8d685c52 100644 --- a/src/main/stats/collector.ts +++ b/src/main/stats/collector.ts @@ -67,6 +67,10 @@ export class StatsCollector { record(event: StatsEvent): void { this.events.push(event) + // A stalled async write must not defer the in-memory retention limit. + if (this.events.length > MAX_EVENTS) { + this.events.splice(0, this.events.length - MAX_EVENTS) + } this.updateAggregates(event) this.scheduleSave() } From 691d9692e69fee60ffb4fb736babdbfde4a580b7 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 20:58:02 -0700 Subject: [PATCH 077/168] fix(pty): stop detached OMP tools on immediate terminal close (#20642) * test(omp): add opt-in owned PTY closure probe * fix(pty): sweep detached tools on immediate unrecognized shell close * test(omp): create close probe evidence root in fresh worktrees * test(pty): account for asynchronous immediate descendant cleanup * test(pty): reject inconclusive descendant cleanup probes --- .../daemon-audit-eligibility-event.test.ts | 1 + ...emon-authenticated-client-activity.test.ts | 1 + .../daemon/daemon-endpoint-ownership.test.ts | 1 + src/main/daemon/daemon-health.test.ts | 1 + src/main/daemon/daemon-idle-shutdown.test.ts | 1 + ...aemon-preflight-client-replacement.test.ts | 1 + ...-pty-adapter-cold-restore-reanchor.test.ts | 1 + ...emon-pty-adapter-cold-restore-seed.test.ts | 1 + ...on-pty-adapter-concurrent-recovery.test.ts | 1 + ...daemon-pty-adapter-daemon-recovery.test.ts | 1 + ...on-pty-adapter-history-checkpoints.test.ts | 1 + ...aemon-pty-adapter-history-recovery.test.ts | 1 + ...emon-pty-adapter-inventory-respawn.test.ts | 1 + ...pty-adapter-protocol-compatibility.test.ts | 1 + ...aemon-pty-adapter-session-adoption.test.ts | 1 + src/main/daemon/daemon-pty-adapter.test.ts | 1 + .../daemon-pty-router-history-handoff.test.ts | 1 + .../daemon-pty-upgrade-adoption.test.ts | 1 + ...emon-reattach-checkpoint-isolation.test.ts | 1 + .../daemon-restore-scrollback-depth.test.ts | 1 + .../daemon-self-retirement-respawn.test.ts | 1 + ...on-server-async-spawn-cancellation.test.ts | 1 + .../daemon/daemon-server-attach-only.test.ts | 1 + ...daemon-server-attachment-lifecycle.test.ts | 1 + .../daemon-server-error-handling.test.ts | 1 + .../daemon-server-kill-attribution.test.ts | 1 + src/main/daemon/daemon-server.test.ts | 1 + .../daemon-session-scrollback-window.test.ts | 1 + ...n-final-checkpoint-caller-deadline.test.ts | 1 + ...emon-stream-droppability-lifecycle.test.ts | 1 + ...aemon-transport-attachment-release.test.ts | 1 + ...6814-daemon-failure-classification.test.ts | 1 + src/main/daemon/mock-descendant-sweep.ts | 8 + src/main/daemon/reattach-snapshot.test.ts | 1 + .../slow-daemon-session-verification.test.ts | 1 + .../terminal-host-agent-session.test.ts | 1 + .../daemon/terminal-host-attach-only.test.ts | 1 + .../terminal-host-process-inspection.test.ts | 1 + .../terminal-host-readiness-reporting.test.ts | 1 + ...terminal-host-session-reaping-leak.test.ts | 16 +- src/main/daemon/terminal-host-startup.test.ts | 1 + .../daemon/terminal-host-wsl-context.test.ts | 1 + .../daemon/terminal-session-teardown.test.ts | 74 +++--- src/main/daemon/terminal-session-teardown.ts | 26 +- .../pty-listener-teardown-and-orphans.test.ts | 19 +- .../local-pty-provider-shutdown.test.ts | 9 +- src/main/providers/local-pty-termination.ts | 15 +- ...escendant-termination-job-coverage.test.ts | 3 +- ...session-host-authority.integration.test.ts | 1 + tests/tools/omp-close-lifecycle.md | 98 ++++++++ tests/tools/omp-close-lifecycle.test.mjs | 224 ++++++++++++++++++ 51 files changed, 459 insertions(+), 74 deletions(-) create mode 100644 src/main/daemon/mock-descendant-sweep.ts create mode 100644 tests/tools/omp-close-lifecycle.md create mode 100644 tests/tools/omp-close-lifecycle.test.mjs diff --git a/src/main/daemon/daemon-audit-eligibility-event.test.ts b/src/main/daemon/daemon-audit-eligibility-event.test.ts index 13d3e0f39ed..0b2c480118f 100644 --- a/src/main/daemon/daemon-audit-eligibility-event.test.ts +++ b/src/main/daemon/daemon-audit-eligibility-event.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' diff --git a/src/main/daemon/daemon-authenticated-client-activity.test.ts b/src/main/daemon/daemon-authenticated-client-activity.test.ts index 6579ced7a47..bc1e63ab8e2 100644 --- a/src/main/daemon/daemon-authenticated-client-activity.test.ts +++ b/src/main/daemon/daemon-authenticated-client-activity.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { readFileSync, mkdtempSync, rmSync } from 'node:fs' import { connect, type Socket } from 'node:net' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/daemon-endpoint-ownership.test.ts b/src/main/daemon/daemon-endpoint-ownership.test.ts index cf6af2e0956..7aff5fb32ee 100644 --- a/src/main/daemon/daemon-endpoint-ownership.test.ts +++ b/src/main/daemon/daemon-endpoint-ownership.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { existsSync, diff --git a/src/main/daemon/daemon-health.test.ts b/src/main/daemon/daemon-health.test.ts index 7d0d64271fa..c1b98c06397 100644 --- a/src/main/daemon/daemon-health.test.ts +++ b/src/main/daemon/daemon-health.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { spawn } from 'node:child_process' diff --git a/src/main/daemon/daemon-idle-shutdown.test.ts b/src/main/daemon/daemon-idle-shutdown.test.ts index 05f4b3aa39f..1500718e98f 100644 --- a/src/main/daemon/daemon-idle-shutdown.test.ts +++ b/src/main/daemon/daemon-idle-shutdown.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { EventEmitter } from 'node:events' import { connect, type Socket } from 'node:net' import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' diff --git a/src/main/daemon/daemon-preflight-client-replacement.test.ts b/src/main/daemon/daemon-preflight-client-replacement.test.ts index 7fbb46ebfd5..726c00a89ed 100644 --- a/src/main/daemon/daemon-preflight-client-replacement.test.ts +++ b/src/main/daemon/daemon-preflight-client-replacement.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { connect, type Socket } from 'node:net' import { join } from 'node:path' diff --git a/src/main/daemon/daemon-pty-adapter-cold-restore-reanchor.test.ts b/src/main/daemon/daemon-pty-adapter-cold-restore-reanchor.test.ts index fd3a7dca13c..b1b3d9a4259 100644 --- a/src/main/daemon/daemon-pty-adapter-cold-restore-reanchor.test.ts +++ b/src/main/daemon/daemon-pty-adapter-cold-restore-reanchor.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' /* Re-anchoring after a cold restore: aliveness probing, sticky restore cache, persistence. */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { join } from 'node:path' diff --git a/src/main/daemon/daemon-pty-adapter-cold-restore-seed.test.ts b/src/main/daemon/daemon-pty-adapter-cold-restore-seed.test.ts index 71e56014a42..4b6c5e74b52 100644 --- a/src/main/daemon/daemon-pty-adapter-cold-restore-seed.test.ts +++ b/src/main/daemon/daemon-pty-adapter-cold-restore-seed.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' /* Cold-restore seed transfer and the payload shapes handed back to the renderer. */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { hostname } from 'node:os' diff --git a/src/main/daemon/daemon-pty-adapter-concurrent-recovery.test.ts b/src/main/daemon/daemon-pty-adapter-concurrent-recovery.test.ts index 12805c0b3ee..66a3320ac81 100644 --- a/src/main/daemon/daemon-pty-adapter-concurrent-recovery.test.ts +++ b/src/main/daemon/daemon-pty-adapter-concurrent-recovery.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { expect, it, vi } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/daemon-pty-adapter-daemon-recovery.test.ts b/src/main/daemon/daemon-pty-adapter-daemon-recovery.test.ts index b53d75b2032..b6bb872c50b 100644 --- a/src/main/daemon/daemon-pty-adapter-daemon-recovery.test.ts +++ b/src/main/daemon/daemon-pty-adapter-daemon-recovery.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' /* Dead-endpoint write handling and daemon respawn after the daemon dies. */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { existsSync, rmSync } from 'node:fs' diff --git a/src/main/daemon/daemon-pty-adapter-history-checkpoints.test.ts b/src/main/daemon/daemon-pty-adapter-history-checkpoints.test.ts index 3bdd53034d6..b0a5612c582 100644 --- a/src/main/daemon/daemon-pty-adapter-history-checkpoints.test.ts +++ b/src/main/daemon/daemon-pty-adapter-history-checkpoints.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' /* Periodic/final history checkpointing: scheduling, work caps, cooldown and shutdown writes. */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { join } from 'node:path' diff --git a/src/main/daemon/daemon-pty-adapter-history-recovery.test.ts b/src/main/daemon/daemon-pty-adapter-history-recovery.test.ts index 4703f11300b..9e2914a6a56 100644 --- a/src/main/daemon/daemon-pty-adapter-history-recovery.test.ts +++ b/src/main/daemon/daemon-pty-adapter-history-recovery.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' /* History recovery / quarantine / reconcile regressions for DaemonPtyAdapter. */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/daemon-pty-adapter-inventory-respawn.test.ts b/src/main/daemon/daemon-pty-adapter-inventory-respawn.test.ts index f53e2c4c410..bccb0cbd19c 100644 --- a/src/main/daemon/daemon-pty-adapter-inventory-respawn.test.ts +++ b/src/main/daemon/daemon-pty-adapter-inventory-respawn.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' /* Inventory after the terminal host dies: worktree removal must not hard-fail. */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { DaemonPtyAdapter } from './daemon-pty-adapter' diff --git a/src/main/daemon/daemon-pty-adapter-protocol-compatibility.test.ts b/src/main/daemon/daemon-pty-adapter-protocol-compatibility.test.ts index a2f2cbf3497..8132f2de4e2 100644 --- a/src/main/daemon/daemon-pty-adapter-protocol-compatibility.test.ts +++ b/src/main/daemon/daemon-pty-adapter-protocol-compatibility.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' /* DaemonPtyAdapter behaviour that varies with the negotiated daemon protocol version. */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { rmSync } from 'node:fs' diff --git a/src/main/daemon/daemon-pty-adapter-session-adoption.test.ts b/src/main/daemon/daemon-pty-adapter-session-adoption.test.ts index cd6acc2352f..a603c21f037 100644 --- a/src/main/daemon/daemon-pty-adapter-session-adoption.test.ts +++ b/src/main/daemon/daemon-pty-adapter-session-adoption.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' /* Adopting daemon sessions that already exist: reattach, attach-only, inventory, tombstones, startup reconcile. */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { join } from 'node:path' diff --git a/src/main/daemon/daemon-pty-adapter.test.ts b/src/main/daemon/daemon-pty-adapter.test.ts index 379d6a23b8f..410caddbe21 100644 --- a/src/main/daemon/daemon-pty-adapter.test.ts +++ b/src/main/daemon/daemon-pty-adapter.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' /* Core IPtyProvider surface of DaemonPtyAdapter: spawn, io, sizing, teardown. */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { rmSync } from 'node:fs' diff --git a/src/main/daemon/daemon-pty-router-history-handoff.test.ts b/src/main/daemon/daemon-pty-router-history-handoff.test.ts index e57375467ef..0a7eead083a 100644 --- a/src/main/daemon/daemon-pty-router-history-handoff.test.ts +++ b/src/main/daemon/daemon-pty-router-history-handoff.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/daemon-pty-upgrade-adoption.test.ts b/src/main/daemon/daemon-pty-upgrade-adoption.test.ts index 99c2a792e1e..7a92aecf150 100644 --- a/src/main/daemon/daemon-pty-upgrade-adoption.test.ts +++ b/src/main/daemon/daemon-pty-upgrade-adoption.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, describe, expect, it, vi } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/daemon-reattach-checkpoint-isolation.test.ts b/src/main/daemon/daemon-reattach-checkpoint-isolation.test.ts index b2c0bf59cbc..d716cb9372d 100644 --- a/src/main/daemon/daemon-reattach-checkpoint-isolation.test.ts +++ b/src/main/daemon/daemon-reattach-checkpoint-isolation.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/daemon-restore-scrollback-depth.test.ts b/src/main/daemon/daemon-restore-scrollback-depth.test.ts index b2c2c7c5028..dead1a7b64f 100644 --- a/src/main/daemon/daemon-restore-scrollback-depth.test.ts +++ b/src/main/daemon/daemon-restore-scrollback-depth.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/daemon-self-retirement-respawn.test.ts b/src/main/daemon/daemon-self-retirement-respawn.test.ts index 7d70d02e70a..7adaa3050fe 100644 --- a/src/main/daemon/daemon-self-retirement-respawn.test.ts +++ b/src/main/daemon/daemon-self-retirement-respawn.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { existsSync, mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' diff --git a/src/main/daemon/daemon-server-async-spawn-cancellation.test.ts b/src/main/daemon/daemon-server-async-spawn-cancellation.test.ts index 57585dc9318..7c42e88c93d 100644 --- a/src/main/daemon/daemon-server-async-spawn-cancellation.test.ts +++ b/src/main/daemon/daemon-server-async-spawn-cancellation.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' diff --git a/src/main/daemon/daemon-server-attach-only.test.ts b/src/main/daemon/daemon-server-attach-only.test.ts index 60e9ee83495..8b4099368a8 100644 --- a/src/main/daemon/daemon-server-attach-only.test.ts +++ b/src/main/daemon/daemon-server-attach-only.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, describe, expect, it, vi } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/daemon-server-attachment-lifecycle.test.ts b/src/main/daemon/daemon-server-attachment-lifecycle.test.ts index 2f4d1b734ac..61243f56920 100644 --- a/src/main/daemon/daemon-server-attachment-lifecycle.test.ts +++ b/src/main/daemon/daemon-server-attachment-lifecycle.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { Socket } from 'node:net' import { mkdtempSync, rmSync } from 'node:fs' diff --git a/src/main/daemon/daemon-server-error-handling.test.ts b/src/main/daemon/daemon-server-error-handling.test.ts index 7ab9d1ffa95..adca3e19834 100644 --- a/src/main/daemon/daemon-server-error-handling.test.ts +++ b/src/main/daemon/daemon-server-error-handling.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { chmodSync, linkSync, mkdtempSync, readFileSync, rmSync, unlinkSync } from 'node:fs' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/daemon-server-kill-attribution.test.ts b/src/main/daemon/daemon-server-kill-attribution.test.ts index b2f506f42ed..d5c0bcf8bd2 100644 --- a/src/main/daemon/daemon-server-kill-attribution.test.ts +++ b/src/main/daemon/daemon-server-kill-attribution.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, describe, expect, it, vi } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/daemon-server.test.ts b/src/main/daemon/daemon-server.test.ts index 27727aa2b3d..ef89b3ef3b0 100644 --- a/src/main/daemon/daemon-server.test.ts +++ b/src/main/daemon/daemon-server.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { connect, type Server, type Socket } from 'node:net' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/daemon-session-scrollback-window.test.ts b/src/main/daemon/daemon-session-scrollback-window.test.ts index b2dfb5118c9..d78ace1a038 100644 --- a/src/main/daemon/daemon-session-scrollback-window.test.ts +++ b/src/main/daemon/daemon-session-scrollback-window.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' /** * OOM regression: a daemon owning 100+ terminals retained ~5000 rows of grid per session with no * bound, grew to ~1.9 GB, and was killed under system memory pressure — losing every session it diff --git a/src/main/daemon/daemon-shutdown-final-checkpoint-caller-deadline.test.ts b/src/main/daemon/daemon-shutdown-final-checkpoint-caller-deadline.test.ts index 64ae0f77ba0..f134a8c8f8c 100644 --- a/src/main/daemon/daemon-shutdown-final-checkpoint-caller-deadline.test.ts +++ b/src/main/daemon/daemon-shutdown-final-checkpoint-caller-deadline.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/daemon-stream-droppability-lifecycle.test.ts b/src/main/daemon/daemon-stream-droppability-lifecycle.test.ts index fda1fe25b98..e65e8ab18ba 100644 --- a/src/main/daemon/daemon-stream-droppability-lifecycle.test.ts +++ b/src/main/daemon/daemon-stream-droppability-lifecycle.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { randomUUID } from 'node:crypto' import type { Socket } from 'node:net' diff --git a/src/main/daemon/daemon-transport-attachment-release.test.ts b/src/main/daemon/daemon-transport-attachment-release.test.ts index 7b7c5e4fee6..511b9bf944e 100644 --- a/src/main/daemon/daemon-transport-attachment-release.test.ts +++ b/src/main/daemon/daemon-transport-attachment-release.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' /** * Attachment-leak regression: an attachment that outlives its transport leaves the session looking * viewed forever — producer pause/resume and any attachment-gated behavior then act on a client that diff --git a/src/main/daemon/issue-6814-daemon-failure-classification.test.ts b/src/main/daemon/issue-6814-daemon-failure-classification.test.ts index 35b84245a83..a7504a5e2a3 100644 --- a/src/main/daemon/issue-6814-daemon-failure-classification.test.ts +++ b/src/main/daemon/issue-6814-daemon-failure-classification.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' // Regression coverage for issue #6814 (terminal lockup after upgrade). // // Drives the real DaemonServer + checkDaemonHealth client over a real unix diff --git a/src/main/daemon/mock-descendant-sweep.ts b/src/main/daemon/mock-descendant-sweep.ts new file mode 100644 index 00000000000..e8baa228c10 --- /dev/null +++ b/src/main/daemon/mock-descendant-sweep.ts @@ -0,0 +1,8 @@ +import { vi } from 'vitest' + +// Mock subprocess PIDs must never reach the host process table or signal real descendants. +vi.mock('../pty-descendant-termination', () => ({ + killWithDescendantSweep: async (_pid: number, killRoot: () => void): Promise => { + killRoot() + } +})) diff --git a/src/main/daemon/reattach-snapshot.test.ts b/src/main/daemon/reattach-snapshot.test.ts index 9bbf7a103d6..c707521223e 100644 --- a/src/main/daemon/reattach-snapshot.test.ts +++ b/src/main/daemon/reattach-snapshot.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, describe, expect, it, vi } from 'vitest' import { TerminalHost } from './terminal-host' import { HeadlessEmulator } from './headless-emulator' diff --git a/src/main/daemon/slow-daemon-session-verification.test.ts b/src/main/daemon/slow-daemon-session-verification.test.ts index 082f1949a5f..9668f1de26d 100644 --- a/src/main/daemon/slow-daemon-session-verification.test.ts +++ b/src/main/daemon/slow-daemon-session-verification.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { connect, createServer, type Server, type Socket } from 'node:net' import { tmpdir } from 'node:os' diff --git a/src/main/daemon/terminal-host-agent-session.test.ts b/src/main/daemon/terminal-host-agent-session.test.ts index 1cf929fb93f..41b2f0c53e3 100644 --- a/src/main/daemon/terminal-host-agent-session.test.ts +++ b/src/main/daemon/terminal-host-agent-session.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { SubprocessHandle } from './session-subprocess-handle' import { TerminalHost } from './terminal-host' diff --git a/src/main/daemon/terminal-host-attach-only.test.ts b/src/main/daemon/terminal-host-attach-only.test.ts index 97284451233..f33630536ee 100644 --- a/src/main/daemon/terminal-host-attach-only.test.ts +++ b/src/main/daemon/terminal-host-attach-only.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from 'vitest' import type { SubprocessHandle } from './session-subprocess-handle' import { TerminalHost, type TerminalHostOptions } from './terminal-host' diff --git a/src/main/daemon/terminal-host-process-inspection.test.ts b/src/main/daemon/terminal-host-process-inspection.test.ts index 50858a89ae1..eca0207dade 100644 --- a/src/main/daemon/terminal-host-process-inspection.test.ts +++ b/src/main/daemon/terminal-host-process-inspection.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi } from 'vitest' import type { SubprocessHandle } from './session-subprocess-handle' import { TerminalHost } from './terminal-host' diff --git a/src/main/daemon/terminal-host-readiness-reporting.test.ts b/src/main/daemon/terminal-host-readiness-reporting.test.ts index 677bcae258a..6e636e766e1 100644 --- a/src/main/daemon/terminal-host-readiness-reporting.test.ts +++ b/src/main/daemon/terminal-host-readiness-reporting.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { SubprocessHandle } from './session-subprocess-handle' import { TerminalHost } from './terminal-host' diff --git a/src/main/daemon/terminal-host-session-reaping-leak.test.ts b/src/main/daemon/terminal-host-session-reaping-leak.test.ts index c937607a33a..4d0c53e1a77 100644 --- a/src/main/daemon/terminal-host-session-reaping-leak.test.ts +++ b/src/main/daemon/terminal-host-session-reaping-leak.test.ts @@ -130,13 +130,21 @@ describe('TerminalHost dead-session reaping (leak regression)', () => { }) lastSubprocess.forceKill = vi.fn() + let releaseSweep = (): void => {} + killWithDescendantSweepMock.mockImplementationOnce( + () => + new Promise((resolve) => { + releaseSweep = resolve + }) + ) const killed = host.kill('session-1', { immediate: true }) - // Immediate teardown skips the graceful kill and force-kills the child directly. On POSIX - // that reaches the child pgroup, so no Windows taskkill /T /F descendant sweep is needed. + expect(killWithDescendantSweepMock).toHaveBeenCalledTimes(1) expect(lastSubprocess.kill).not.toHaveBeenCalled() - expect(lastSubprocess.forceKill).toHaveBeenCalled() - expect(killWithDescendantSweepMock).not.toHaveBeenCalled() + expect(lastSubprocess.forceKill).not.toHaveBeenCalled() + expect(emulatorDispose).not.toHaveBeenCalled() + releaseSweep() + await vi.waitFor(() => expect(lastSubprocess.forceKill).toHaveBeenCalledTimes(1)) expect(emulatorDispose).not.toHaveBeenCalled() expect(host.listSessions()).toHaveLength(1) lastSubprocess._onExitCb?.(137) diff --git a/src/main/daemon/terminal-host-startup.test.ts b/src/main/daemon/terminal-host-startup.test.ts index e7b7c7b4c09..c6817b1e758 100644 --- a/src/main/daemon/terminal-host-startup.test.ts +++ b/src/main/daemon/terminal-host-startup.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { TerminalHost } from './terminal-host' import type { SubprocessHandle } from './session-subprocess-handle' diff --git a/src/main/daemon/terminal-host-wsl-context.test.ts b/src/main/daemon/terminal-host-wsl-context.test.ts index 3958e370684..de0523b7ff1 100644 --- a/src/main/daemon/terminal-host-wsl-context.test.ts +++ b/src/main/daemon/terminal-host-wsl-context.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, describe, expect, it, vi } from 'vitest' import type * as WslModule from '../wsl' diff --git a/src/main/daemon/terminal-session-teardown.test.ts b/src/main/daemon/terminal-session-teardown.test.ts index 95010efd106..aeb9c7e31d2 100644 --- a/src/main/daemon/terminal-session-teardown.test.ts +++ b/src/main/daemon/terminal-session-teardown.test.ts @@ -61,50 +61,60 @@ describe('TerminalSessionTeardown plain-shell teardown', () => { expect(() => killRoot()).not.toThrow() }) - it('win32 immediate kill claims termination before awaiting the sweep', async () => { - // Why: createOrAttach rejects a doomed plain shell only via isTerminating, so the claim - // must land before the taskkill await or an attach can bind a pane to a dying session. - setPlatform('win32') - const session = createPlainShellSession() - const beginTermination = session.beginTermination as unknown as ReturnType - let claimedBeforeSweep = false - killWithDescendantSweepMock.mockImplementation(async () => { - claimedBeforeSweep = beginTermination.mock.calls.length === 1 - }) - const teardown = new TerminalSessionTeardown(new Map([['s1', session]])) + it.each(['win32', 'linux', 'darwin'] as const)( + '%s immediate kill claims termination before awaiting the sweep', + async (platform) => { + // Why: createOrAttach rejects a doomed plain shell only via isTerminating, so the claim + // must land before the taskkill await or an attach can bind a pane to a dying session. + setPlatform(platform) + const session = createPlainShellSession() + const beginTermination = session.beginTermination as unknown as ReturnType + let claimedBeforeSweep = false + killWithDescendantSweepMock.mockImplementation(async () => { + claimedBeforeSweep = beginTermination.mock.calls.length === 1 + }) + const teardown = new TerminalSessionTeardown(new Map([['s1', session]])) - await teardown.killSession('s1', session, true) + await teardown.killSession('s1', session, true) - expect(claimedBeforeSweep).toBe(true) - }) + expect(claimedBeforeSweep).toBe(true) + } + ) - it('win32 sweep ownsRoot guard requires the live session to still own the id', async () => { - setPlatform('win32') - const session = createPlainShellSession() - const sessions = new Map([['s1', session]]) - const teardown = new TerminalSessionTeardown(sessions) + it.each(['win32', 'linux', 'darwin'] as const)( + '%s sweep ownsRoot guard requires the live session to still own the id', + async (platform) => { + setPlatform(platform) + const session = createPlainShellSession() + const sessions = new Map([['s1', session]]) + const teardown = new TerminalSessionTeardown(sessions) - await teardown.killSession('s1', session, true) - const ownsRoot = (killWithDescendantSweepMock.mock.calls[0][2] as { ownsRoot: () => boolean }) - .ownsRoot - expect(ownsRoot()).toBe(true) + await teardown.killSession('s1', session, true) + const ownsRoot = (killWithDescendantSweepMock.mock.calls[0][2] as { ownsRoot: () => boolean }) + .ownsRoot + expect(ownsRoot()).toBe(true) - // A natural exit or reap must stop us from taskkilling a recycled PID. - ;(session as unknown as { isAlive: boolean }).isAlive = false - expect(ownsRoot()).toBe(false) - sessions.delete('s1') - ;(session as unknown as { isAlive: boolean }).isAlive = true - expect(ownsRoot()).toBe(false) - }) + // A natural exit or reap must stop us from taskkilling a recycled PID. + ;(session as unknown as { isAlive: boolean }).isAlive = false + expect(ownsRoot()).toBe(false) + sessions.delete('s1') + ;(session as unknown as { isAlive: boolean }).isAlive = true + expect(ownsRoot()).toBe(false) + } + ) - it('non-win32 immediate kill skips the tree kill (pgroup force-kill suffices)', async () => { + it('POSIX immediate close sweeps detached OMP tools before killing their parent', async () => { setPlatform('linux') const session = createPlainShellSession() const teardown = new TerminalSessionTeardown(new Map([['s1', session]])) await teardown.killSession('s1', session, true) - expect(killWithDescendantSweepMock).not.toHaveBeenCalled() + expect(killWithDescendantSweepMock).toHaveBeenCalledWith( + session.pid, + expect.any(Function), + expect.objectContaining({ ownsRoot: expect.any(Function) }) + ) expect(session.forceKillAndWaitForExit).toHaveBeenCalled() }) diff --git a/src/main/daemon/terminal-session-teardown.ts b/src/main/daemon/terminal-session-teardown.ts index 017f841a5e0..d35bfc4d64c 100644 --- a/src/main/daemon/terminal-session-teardown.ts +++ b/src/main/daemon/terminal-session-teardown.ts @@ -80,27 +80,13 @@ export class TerminalSessionTeardown { return operation } - /** - * Immediate teardown of a non-agent shell. On Windows, closing the ConPTY does not - * reap orphaned children (node-pty `useConptyDll` skips the console-process reap), so a - * live `pnpm i`/`node` survives shell exit, keeps the ConPTY console non-empty, and holds - * the worktree cwd — failing destructive worktree removal with "Failed to physically stop - * every PTY". Tree-kill only when the OS identity probe returns `own`; `unknown`/`foreign`/ - * `absent` skip taskkill and rely on root close alone. Mirrors the agent path - * (#10004/#10100). POSIX shells already reach their child pgroup on forceKill, so they - * stay on the plain force-kill path. - */ + /** Immediate close must reach detached tools even when startup did not identify an agent. */ private async forceKillPlainShellSession(sessionId: string, session: Session): Promise { - if (process.platform === 'win32') { - // Why: forceKillAndWaitForExit claims termination synchronously; awaiting the sweep - // ahead of it would leave attach open on a doomed session for the taskkill's duration. - session.beginTermination() - await killWithDescendantSweep(session.pid, () => {}, { - // Why: the descendant tree is only ours while this Session still owns the live root PID. - ownsRoot: () => this.sessions.get(sessionId) === session && session.isAlive, - terminateOwnedTree: () => session.terminateOwnedTree() - }) - } + session.beginTermination() + await killWithDescendantSweep(session.pid, () => {}, { + ownsRoot: () => this.sessions.get(sessionId) === session && session.isAlive, + terminateOwnedTree: () => session.terminateOwnedTree() + }) await session.forceKillAndWaitForExit() } diff --git a/src/main/ipc/pty-listener-teardown-and-orphans.test.ts b/src/main/ipc/pty-listener-teardown-and-orphans.test.ts index ae749f8dbe0..7e4178f35d3 100644 --- a/src/main/ipc/pty-listener-teardown-and-orphans.test.ts +++ b/src/main/ipc/pty-listener-teardown-and-orphans.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it, vi } from 'vitest' import { existsSyncMock, + loginPreflightExecFileMock, spawnMock, openCodeClearPtyMock, piClearPtyMock @@ -162,9 +163,25 @@ describe('registerPtyHandlers', () => { rows: 24 })) as { id: string } + let finishSnapshot: (() => void) | undefined + loginPreflightExecFileMock.mockImplementationOnce( + ( + _file: string, + _args: string[], + _options: unknown, + callback: (error: Error | null, stdout: string) => void + ) => { + finishSnapshot = () => callback(null, '') + } + ) const killPromise = handlers.get('pty:kill')!(null, { id: spawnResult.id }) as Promise - expect(killSpy).toHaveBeenCalledTimes(1) + await vi.waitFor(() => expect(finishSnapshot).toBeTypeOf('function')) + expect(killSpy).not.toHaveBeenCalled() + expect(onDataDisposable.dispose).not.toHaveBeenCalled() + expect(onExitDisposable.dispose).not.toHaveBeenCalled() + finishSnapshot?.() + await vi.waitFor(() => expect(killSpy).toHaveBeenCalledTimes(1)) expect(onDataDisposable.dispose).not.toHaveBeenCalled() expect(onExitDisposable.dispose).not.toHaveBeenCalled() diff --git a/src/main/providers/local-pty-provider-shutdown.test.ts b/src/main/providers/local-pty-provider-shutdown.test.ts index cb7595800c1..cccede7186b 100644 --- a/src/main/providers/local-pty-provider-shutdown.test.ts +++ b/src/main/providers/local-pty-provider-shutdown.test.ts @@ -513,13 +513,16 @@ describe('LocalPtyProvider', () => { expect(killWithDescendantSweepMock).not.toHaveBeenCalled() }) - it('non-win32 immediate shutdown of a plain shell skips the tree kill', async () => { - // beforeEach pins platform to linux; POSIX force-kill already reaches the child pgroup. + it('POSIX immediate shutdown sweeps detached OMP tools without startup recognition', async () => { const { id } = await provider.spawn({ cols: 80, rows: 24 }) await provider.shutdown(id, { immediate: true }) - expect(killWithDescendantSweepMock).not.toHaveBeenCalled() + expect(killWithDescendantSweepMock).toHaveBeenCalledWith( + mockProc.pid, + expect.any(Function), + expect.objectContaining({ ownsRoot: expect.any(Function) }) + ) }) }) diff --git a/src/main/providers/local-pty-termination.ts b/src/main/providers/local-pty-termination.ts index 470d5b600ab..7a19e9d7326 100644 --- a/src/main/providers/local-pty-termination.ts +++ b/src/main/providers/local-pty-termination.ts @@ -175,19 +175,8 @@ async function shutdownTrackedPty( operation.rootSignalled = true requestTrackedPtyShutdown(id, proc, operation.immediate) } - if (ptyAgentSessionIds.has(id)) { - // Why: POSIX needs a pre-kill descendant snapshot; Windows tree-kills only when the - // identity probe returns `own` so agent/MCP orphans cannot hold the worktree cwd - // (#10004). `unknown`/`foreign`/`absent` skip taskkill and rely on root close alone. - await killWithDescendantSweep(proc.pid, signalRoot, { - ownsRoot: () => ptyProcesses.get(id) === proc, - terminateOwnedTree: () => terminatePtyJob(proc) - }) - } else if (process.platform === 'win32' && operation.immediate) { - // Why: a plain shell's ConPTY teardown doesn't reap orphaned children (useConptyDll - // skips the console reap), so a live `pnpm i`/`node` keeps the ConPTY console alive and - // holds the worktree cwd. Tree kill runs only when the OS identity probe returns `own`; - // otherwise root close alone, and detached children may block physical stop (#10004). + if (ptyAgentSessionIds.has(id) || operation.immediate) { + // Typed agents also detach tool process groups; immediate close must snapshot before root exit. await killWithDescendantSweep(proc.pid, signalRoot, { ownsRoot: () => ptyProcesses.get(id) === proc, terminateOwnedTree: () => terminatePtyJob(proc) diff --git a/src/main/pty-descendant-termination-job-coverage.test.ts b/src/main/pty-descendant-termination-job-coverage.test.ts index 21e14012caa..738d16ab16f 100644 --- a/src/main/pty-descendant-termination-job-coverage.test.ts +++ b/src/main/pty-descendant-termination-job-coverage.test.ts @@ -16,7 +16,8 @@ import { describe, expect, it } from 'vitest' */ const SRC_DIR = join(__dirname, '..') const CALL = 'killWithDescendantSweep(' -const EXPECTED_MINIMUM_SITES = 5 +// Local immediate and recognized-agent shutdown share one guarded call site. +const EXPECTED_MINIMUM_SITES = 4 function collectTypeScriptFiles(dir: string): string[] { const found: string[] = [] diff --git a/src/main/runtime/remote-agent-session-host-authority.integration.test.ts b/src/main/runtime/remote-agent-session-host-authority.integration.test.ts index 56d74744238..884a80a36fb 100644 --- a/src/main/runtime/remote-agent-session-host-authority.integration.test.ts +++ b/src/main/runtime/remote-agent-session-host-authority.integration.test.ts @@ -1,3 +1,4 @@ +import '../daemon/mock-descendant-sweep' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' diff --git a/tests/tools/omp-close-lifecycle.md b/tests/tools/omp-close-lifecycle.md new file mode 100644 index 00000000000..03e2dd6fc31 --- /dev/null +++ b/tests/tools/omp-close-lifecycle.md @@ -0,0 +1,98 @@ +# OMP owned-PTY close probe (#9530) + +This opt-in probe launches an actual installed OMP binary in disposable local PTYs +and calls Orca's production `shutdownLocalPty` and `killAllLocalPtys` functions, +or daemon `Session`, native subprocess handle, and `TerminalSessionTeardown`. +It sets the same agent-session ownership flag that `activateLocalPtySession` sets +for `launchAgent` / recognized startup commands, then repeats without that flag +to represent OMP typed into a shell. This isolates termination policy; it does not +exercise Agent button delivery or terminal-tab/handle routing. + +```sh +ORCA_BACKGROUND_LAUNCH=1 ORCA_OMP_PROBE_BINARY=/absolute/path/to/omp \ + node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts \ + tests/tools/omp-close-lifecycle.test.mjs +``` + +The probe defaults to zsh on macOS and bash on other POSIX hosts. Set +`ORCA_OMP_PROBE_SHELL` to the absolute path of either shell to override. Windows +is skipped. It requires the existing node-pty native dependency for the current +Node runtime. The normal unit suite skips the test unless a binary is supplied. + +Each case waits five seconds for OMP startup, captures the owned process tree, +requests explicit close or local quit cleanup, and verifies those exact process +IDs are absent using host `ps` after a six-second observation window. It records +raw terminal output and before/after process rows in `.bench-fixtures/omp-close-*`. +The fixture contains no prompt or model request. It disables the first-run setup +wizard, startup splash and update checks in a temporary config; OMP's normal tools +and extensions remain enabled. HOME, ZDOTDIR, XDG_CONFIG_HOME and OMP's agent home +are disposable. Cleanup signals only owned identities with matching process start +time and group, then removes the temporary home. + +## Observed on 2026-09-14 + +At Orca base `93c370246388`, macOS arm64, installed `omp/18.1.18`: + +- Explicit local close with the agent flag: shell and foreground OMP exited. +- Explicit local close without the flag: shell and foreground OMP exited. +- Local quit cleanup with or without the flag: shell and foreground OMP exited. +- Explicit close used the existing five-second force deadline for the shell. + Quit removes native exit tracking immediately, so the probe uses independent + host process evidence; an empty provider map is not its exit oracle. + +The same four outcomes were observed in an initial first-run setup-splash pass. +The normal-idle transcript displayed the OMP prompt and reported no LSP servers. +No stale foreground OMP was reproduced in these local termination-policy cases. + +## Detached external tool reproduction and correction + +Set `ORCA_OMP_PROBE_EXTERNAL_TOOL=1` to run `! /bin/sleep 120` in OMP before +explicit immediate close. Add `ORCA_OMP_PROBE_BACKEND=daemon` to exercise the daemon +backend. Each mode tests both recognized and typed launches; these modes do not +run the local-quit cases. The probe makes no model requests. Both OMP/PI profiles +are cleared, and XDG data/cache/state roots are isolated alongside configuration. + +On macOS, installed Orca `1.4.202-hourly.202609132311` and OMP `18.1.18`, an actual +non-focus CLI-created terminal reproduced the detached-child leak: shell PID +71632 and OMP PID 71667 exited after CLI close, but sleep PID 72125 (PGID 72125) +remained after the grace window, reparented to PID 1. The owned survivor was +cleaned using its captured PID/start-time/group identity. This is a detached-tool +leak, not a reproduction of the reported foreground OMP surviving for days. + +With the correction, all four actual OMP/external-sleep cases (recognized/typed, +local/daemon) left none of the captured shell, OMP or sleep PIDs present. This +runs production backend code with real PTYs; it does not run a rebuilt installed +app through the CLI. Reports/transcripts remain local under `.bench-fixtures/`. + +### Termination contract + +Immediate close now uses the existing descendant sweep for all local-provider +and daemon shells, including agents typed after startup. This also terminates +still-parented, intentionally detached jobs that previously survived POSIX close. +The sweep captures descendants before root exit, checks current root ownership, +and retains the existing identity-guarded delayed escalation. It adds a bounded +process-table capture (one-second timeout) and, when descendants exist, the +existing single two-second delayed recheck; there is no recurring polling. +Daemon termination is claimed before awaiting capture, preventing reattachment. +Physical root exit still gates session reaping. Snapshot failure falls back to +root termination; children already reparented before capture are not covered. + +The execution host runs this policy. Paired runtimes using these backends receive +the fix when their host updates; no wire fields or client-side remote PID signals +are added. Direct SSH relay PTYs use separate `src/relay/pty-handler.ts` termination +and are not fixed or runtime-validated by this change. Graceful plain-shell +shutdown, disconnect and daemon/remote keep-alive policy are unchanged. The code +uses no repository metadata and applies to folder workspaces as well as worktrees. +Windows retains its existing guarded job/tree termination; this probe skips it. + +## Limits and next evidence + +Do not close #9530 from this probe. The original report did not identify Orca/OMP +versions or the exact close action. A tab can disappear without this termination +entry point running, which this probe does not cover. It also does not exercise +full app quit lifecycle, background/floating/mobile handle resolution, a busy +model turn, initialized eval workers or LSPs, Windows/WSL/Linux execution, or live SSH ownership. Daemon/remote keep-alive is intentional and remains unchanged. + +A failing reproduction needs the original surface/close action, provider mode, +owning runtime, and process identities before and after. Signal-resistant fixture +processes alone do not establish that current OMP has the reported leak. diff --git a/tests/tools/omp-close-lifecycle.test.mjs b/tests/tools/omp-close-lifecycle.test.mjs new file mode 100644 index 00000000000..47df8325c56 --- /dev/null +++ b/tests/tools/omp-close-lifecycle.test.mjs @@ -0,0 +1,224 @@ +import { it, expect } from 'vitest' +import * as pty from 'node-pty' +import { Session } from '../../src/main/daemon/session.ts' +import { TerminalSessionTeardown } from '../../src/main/daemon/terminal-session-teardown.ts' +import { createDaemonPtySubprocessHandle } from '../../src/main/daemon/pty-subprocess/subprocess-handle.ts' +import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs' +import { join } from 'node:path' +import { tmpdir } from 'node:os' +import { runProcess } from '../../src/shared/child-process/run-process.ts' +import { + captureDescendantSnapshot, + readProcessTable +} from '../../src/main/pty-descendant-termination.ts' +import { + createPtyPhysicalExit, + shutdownLocalPty, + killAllLocalPtys +} from '../../src/main/providers/local-pty-termination.ts' +import { + ptyProcesses, + ptyAgentSessionIds, + ptyPhysicalExits, + ptyExitDisposables, + clearPtyState +} from '../../src/main/providers/local-pty-provider-state.ts' + +const binary = process.env.ORCA_OMP_PROBE_BINARY +const externalTool = process.env.ORCA_OMP_PROBE_EXTERNAL_TOOL === '1' +const daemonBackend = process.env.ORCA_OMP_PROBE_BACKEND === 'daemon' +const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) +const quote = (value) => `'${value.replaceAll("'", "'\\''")}'` +const ownedPidRows = async (pids) => { + const result = await runProcess({ + program: 'ps', + args: ['-p', pids.join(','), '-o', 'pid=,ppid=,pgid=,stat=,comm='], + maxOutputBytes: 16000 + }) + expect(result.timedOut).toBe(false) + expect(result.signal).toBeNull() + expect(result.stderr.trim()).toBe('') + expect([0, 1]).toContain(result.code) + if (result.code === 1) { + expect(result.stdout.trim()).toBe('') + } + return result.stdout.trim() +} +it.skipIf(!binary || process.platform === 'win32')( + 'observes actual OMP under production owned-PTY closure policy', + async () => { + const fixtures = join(process.cwd(), '.bench-fixtures') + mkdirSync(fixtures, { recursive: true }) + const output = mkdtempSync(join(fixtures, 'omp-close-')) + const report = [] + for (const launch of ['recognized', 'typed']) { + for (const close of externalTool || daemonBackend ? ['explicit'] : ['explicit', 'quit']) { + expect(ptyProcesses.size).toBe(0) + const home = mkdtempSync(join(tmpdir(), 'orca-omp-close-home-')) + const agentHome = join(home, 'agent') + mkdirSync(agentHome) + const config = join(home, 'probe.yml') + writeFileSync( + config, + 'startup:\n setupWizard: false\n showSplash: false\n checkUpdate: false\n' + ) + const id = `${launch}-${close}` + let transcript = '' + let nativeExit = null + const shell = + process.env.ORCA_OMP_PROBE_SHELL ?? + (process.platform === 'darwin' ? '/bin/zsh' : '/bin/bash') + const shellArgs = shell.endsWith('zsh') ? ['-f', '-i'] : ['--noprofile', '--norc', '-i'] + const proc = pty.spawn(shell, shellArgs, { + name: 'xterm-256color', + cols: 120, + rows: 35, + cwd: home, + env: { + ...process.env, + HOME: home, + USERPROFILE: home, + ZDOTDIR: home, + XDG_CONFIG_HOME: join(home, 'config'), + XDG_DATA_HOME: join(home, 'data'), + XDG_CACHE_HOME: join(home, 'cache'), + XDG_STATE_HOME: join(home, 'state'), + OMP_CODING_AGENT_DIR: agentHome, + PI_CODING_AGENT_DIR: agentHome, + OMP_PROFILE: '', + PI_PROFILE: '', + PI_CONFIG_DIR: '.omp', + PI_CONFIG_FILES: '', + ORCA_BACKGROUND_LAUNCH: '1' + } + }) + const daemonSession = daemonBackend + ? new Session({ + sessionId: id, + cols: 120, + rows: 35, + shellReadySupported: false, + ...(launch === 'recognized' ? { launchAgent: 'omp' } : {}), + subprocess: createDaemonPtySubprocessHandle({ + process: proc, + shellPath: shell, + spawnCwd: home, + env: process.env, + startupCommandDeliveredInShellArgs: false, + reportsChildExitStatus: true, + sessionId: id, + startupAgentRecognition: null + }) + }) + : null + proc.onData((data) => { + transcript = (transcript + data).slice(-131072) + }) + if (!daemonSession) { + ptyProcesses.set(id, proc) + createPtyPhysicalExit(id) + if (launch === 'recognized') { + ptyAgentSessionIds.add(id) + } + } + ptyExitDisposables.set( + id, + proc.onExit((event) => { + nativeExit = event + ptyPhysicalExits.get(id)?.markExited() + clearPtyState(id) + rmSync(home, { recursive: true, force: true }) + }) + ) + let snapshot + try { + proc.write(`${quote(binary)} --no-session --config ${quote(config)}\r`) + await delay(5000) + snapshot = await captureDescendantSnapshot(proc.pid) + expect(snapshot?.descendants.length).toBeGreaterThan(0) + if (externalTool) { + proc.write('! /bin/sleep 120\r') + for (let attempt = 0; attempt < 25; attempt++) { + await delay(200) + snapshot = await captureDescendantSnapshot(proc.pid) + if (snapshot?.descendants.length > 1) { + break + } + } + expect(snapshot?.descendants.length).toBeGreaterThan(1) + } + const pids = [proc.pid, ...snapshot.descendants.map((row) => row.pid)] + const before = await ownedPidRows(pids) + expect(before).toContain('omp') + if (externalTool) { + expect(before).toContain('sleep') + } + const started = Date.now() + let closeError = null + try { + if (daemonSession) { + await new TerminalSessionTeardown(new Map([[id, daemonSession]])).killSession( + id, + daemonSession, + true + ) + } else if (close === 'explicit') { + await shutdownLocalPty(id, { immediate: externalTool }) + } else { + killAllLocalPtys() + } + } catch (error) { + closeError = String(error) + } + await delay(6000) + const after = await ownedPidRows(pids) + report.push({ + launch, + close, + externalTool, + backend: daemonBackend ? 'daemon' : 'local', + before, + after, + nativeExit, + tracked: daemonSession ? daemonSession.isAlive : ptyProcesses.has(id), + elapsedMs: Date.now() - started, + closeError, + home + }) + writeFileSync(join(output, `${id}.txt`), transcript) + writeFileSync(join(output, 'report.json'), JSON.stringify(report, null, 2)) + expect(closeError).toBeNull() + expect(after).toBe('') + } finally { + if (snapshot) { + const current = await readProcessTable() + const owned = [ + ...snapshot.descendants, + ...(snapshot.root ? [{ ...snapshot.root, pgid: snapshot.rootPgid }] : []) + ] + for (const row of current.rows) { + if ( + owned.some( + (known) => + known.pid === row.pid && + known.startedAt === row.startedAt && + known.pgid === row.pgid + ) + ) { + try { + process.kill(row.pid, 'SIGKILL') + } catch {} + } + } + } + daemonSession?.dispose() + clearPtyState(id) + rmSync(home, { recursive: true, force: true }) + } + } + } + writeFileSync(join(output, 'report.json'), JSON.stringify(report, null, 2)) + console.log(output) + }, + 90000 +) From 8c6ae79e94a8c86b3ed05b4dd46f2f45ae0d5d33 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 20:58:27 -0700 Subject: [PATCH 078/168] fix(relay): stop detached tools on immediate terminal close (#20645) * fix(relay): sweep detached tools on immediate terminal close * test(relay): reject failed process cleanup queries --- src/relay/mock-descendant-sweep.ts | 8 + src/relay/pty-handler-attach-replay.test.ts | 1 + .../pty-handler-dispose-lifecycle.test.ts | 1 + src/relay/pty-handler-grace-timer.test.ts | 1 + .../pty-handler-immediate-descendants.test.ts | 282 ++++++++++++++++++ ...handler-inventory-process-evidence.test.ts | 1 + ...-handler-output-drain-differential.test.ts | 1 + .../pty-handler-output-streaming.test.ts | 1 + .../pty-handler-ownership-attestation.test.ts | 1 + .../pty-handler-resize-stale-pty.test.ts | 1 + .../pty-handler-retired-pane-surface.test.ts | 1 + src/relay/pty-handler-revive.test.ts | 1 + .../pty-handler-shell-resolution.test.ts | 1 + .../pty-handler-shutdown-signals.test.ts | 1 + .../pty-handler-source-publication.test.ts | 1 + src/relay/pty-handler-spawn-admission.test.ts | 1 + src/relay/pty-handler-spawn-cwd.test.ts | 1 + .../pty-handler-spawn-environment.test.ts | 1 + ...y-handler-startup-command-delivery.test.ts | 1 + ...ler-windows-child-process-evidence.test.ts | 1 + src/relay/pty-handler.ts | 52 +++- src/relay/relay-daemon-fatal-reap.test.ts | 1 + tests/tools/omp-relay-close-lifecycle.md | 67 +++++ .../tools/omp-relay-close-lifecycle.test.mjs | 150 ++++++++++ 24 files changed, 575 insertions(+), 3 deletions(-) create mode 100644 src/relay/mock-descendant-sweep.ts create mode 100644 src/relay/pty-handler-immediate-descendants.test.ts create mode 100644 tests/tools/omp-relay-close-lifecycle.md create mode 100644 tests/tools/omp-relay-close-lifecycle.test.mjs diff --git a/src/relay/mock-descendant-sweep.ts b/src/relay/mock-descendant-sweep.ts new file mode 100644 index 00000000000..8af046417f9 --- /dev/null +++ b/src/relay/mock-descendant-sweep.ts @@ -0,0 +1,8 @@ +import { vi } from 'vitest' + +// Mock PTYs reuse the runner PID; never enumerate or signal its real descendants. +vi.mock('../main/pty-descendant-termination', () => ({ + killWithDescendantSweep: async (_pid: number, killRoot: () => void): Promise => { + killRoot() + } +})) diff --git a/src/relay/pty-handler-attach-replay.test.ts b/src/relay/pty-handler-attach-replay.test.ts index e289547bf0c..5ec55a6eb2a 100644 --- a/src/relay/pty-handler-attach-replay.test.ts +++ b/src/relay/pty-handler-attach-replay.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' import * as ptyShellUtils from './pty-shell-utils' import { diff --git a/src/relay/pty-handler-dispose-lifecycle.test.ts b/src/relay/pty-handler-dispose-lifecycle.test.ts index 0553f92a4e5..631834ab1b7 100644 --- a/src/relay/pty-handler-dispose-lifecycle.test.ts +++ b/src/relay/pty-handler-dispose-lifecycle.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' const { mockPtySpawn, mockPtyInstance, mockCreateShellPromptReadinessProbe } = vi.hoisted(() => ({ diff --git a/src/relay/pty-handler-grace-timer.test.ts b/src/relay/pty-handler-grace-timer.test.ts index c1797583c14..ed552e71551 100644 --- a/src/relay/pty-handler-grace-timer.test.ts +++ b/src/relay/pty-handler-grace-timer.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' import { DEFAULT_BOUNDED_SSH_RELAY_GRACE_PERIOD_SECONDS } from '../shared/ssh-types' diff --git a/src/relay/pty-handler-immediate-descendants.test.ts b/src/relay/pty-handler-immediate-descendants.test.ts new file mode 100644 index 00000000000..1362052834a --- /dev/null +++ b/src/relay/pty-handler-immediate-descendants.test.ts @@ -0,0 +1,282 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { beginPtyHandlerTest, endPtyHandlerTest } from './pty-handler-test-harness' +import type { MockDispatcher } from './pty-handler-test-harness' +import type { PtyHandler } from './pty-handler' +import type { RelayPtySourcePublication } from './relay-pty-source-publication' + +const { mockPtySpawn, mockPtyInstance, mockCreateShellPromptReadinessProbe, sweep } = vi.hoisted( + () => ({ + mockPtySpawn: vi.fn(), + mockCreateShellPromptReadinessProbe: vi.fn(), + sweep: + vi.fn< + (pid: number, killRoot: () => void, deps?: { ownsRoot?: () => boolean }) => Promise + >(), + mockPtyInstance: { + pid: process.pid, + onData: vi.fn(), + onExit: vi.fn(), + write: vi.fn(), + resize: vi.fn(), + kill: vi.fn(), + clear: vi.fn(), + pause: vi.fn(), + resume: vi.fn() + } + }) +) +vi.mock('node-pty', () => ({ spawn: mockPtySpawn })) +vi.mock('../main/pty-descendant-termination', () => ({ killWithDescendantSweep: sweep })) +vi.mock('../main/pty/posix-pty-process-groups', () => ({ + forceKillPosixPtyProcessGroups: (_pid: number, kill: () => void) => kill() +})) +vi.mock('../main/shell-prompt-readiness-probe', () => ({ + createShellPromptReadinessProbe: mockCreateShellPromptReadinessProbe +})) + +const ensure = { + claim: { + digestVersion: 1, + keyId: 'claim-key', + identityDigest: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + worktreeScopeDigest: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + agent: 'omp' + }, + surface: { + worktreeId: 'repo::/tmp/worktree', + tabId: '11111111-1111-4111-8111-111111111111', + leafId: '22222222-2222-4222-8222-222222222222', + terminalHandle: 'term_omp' + } +} + +describe('relay immediate descendant cleanup', () => { + let dispatcher: MockDispatcher + let handler: PtyHandler + let originalPlatform: PropertyDescriptor | undefined + let exit: ((event: { exitCode: number }) => void) | undefined + let release: (() => void) | undefined + let kill: ReturnType + + beforeEach(() => { + ;({ dispatcher, handler, originalPlatform } = beginPtyHandlerTest({ + mockPtySpawn, + mockPtyInstance, + mockCreateShellPromptReadinessProbe + })) + exit = undefined + release = undefined + kill = vi.fn() + mockPtySpawn.mockReturnValue({ + ...mockPtyInstance, + kill, + onExit: (callback: (event: { exitCode: number }) => void) => { + exit = callback + } + }) + sweep.mockReset() + sweep.mockImplementation( + (_pid, killRoot) => + new Promise((resolve, reject) => { + release = () => { + try { + killRoot() + resolve() + } catch (error) { + reject(error) + } + } + }) + ) + }) + afterEach(async () => { + release?.() + exit?.({ exitCode: 137 }) + await endPtyHandlerTest(handler, originalPlatform) + }) + + async function spawn(params: Record = {}) { + const result = await dispatcher.callRequest('pty.spawn', params) + if ( + !result || + typeof result !== 'object' || + !('id' in result) || + typeof result.id !== 'string' + ) { + throw new Error('missing PTY id') + } + return result.id + } + const close = (id: string) => dispatcher.callRequest('pty.shutdown', { id, immediate: true }) + + it('sweeps a typed agent before force-kill and joins close through physical exit', async () => { + const id = await spawn() + const first = close(id) + const second = close(id) + expect(sweep).toHaveBeenCalledTimes(1) + expect(kill).not.toHaveBeenCalled() + await expect(dispatcher.callRequest('pty.attach', { id })).rejects.toThrow('terminating') + release?.() + await vi.waitFor(() => expect(kill).toHaveBeenCalledWith('SIGKILL')) + expect(handler.activePtyCount).toBe(1) + exit?.({ exitCode: 137 }) + await Promise.all([first, second]) + expect(handler.activePtyCount).toBe(0) + expect(kill).toHaveBeenCalledTimes(1) + }) + + it('does not signal a root that exits while its snapshot is pending', async () => { + const id = await spawn() + const closing = close(id) + const ownsRoot = sweep.mock.calls[0]?.[2]?.ownsRoot + expect(ownsRoot?.()).toBe(true) + exit?.({ exitCode: 0 }) + expect(ownsRoot?.()).toBe(false) + release?.() + await closing + expect(kill).not.toHaveBeenCalled() + }) + + it('retains the agent claim instead of adopting or duplicating a closing owner', async () => { + const id = await spawn({ agentSessionEnsure: ensure }) + const closing = close(id) + await expect(spawn({ agentSessionEnsure: ensure })).rejects.toThrow('terminating') + expect(mockPtySpawn).toHaveBeenCalledTimes(1) + release?.() + await vi.waitFor(() => expect(kill).toHaveBeenCalledTimes(1)) + exit?.({ exitCode: 137 }) + await closing + }) + + it('does not replay a completed create operation while its owner is closing', async () => { + const params = { + agentSessionEnsure: ensure, + agentSessionCreateOperationId: 'ccccccccccccccccccccccccccccccccccccccccccc' + } + const id = await spawn(params) + const closing = close(id) + await expect(spawn(params)).rejects.toThrow('terminating') + expect(mockPtySpawn).toHaveBeenCalledTimes(1) + release?.() + await vi.waitFor(() => expect(kill).toHaveBeenCalledTimes(1)) + exit?.({ exitCode: 137 }) + await closing + }) + + it('allows retry after a failed root signal without releasing the live PTY', async () => { + const id = await spawn() + kill.mockImplementationOnce(() => { + throw new Error('signal refused') + }) + const rejected = expect(close(id)).rejects.toThrow('signal refused') + release?.() + await rejected + expect(handler.activePtyCount).toBe(1) + const retry = close(id) + expect(sweep).toHaveBeenCalledTimes(2) + release?.() + await vi.waitFor(() => expect(kill).toHaveBeenCalledTimes(2)) + exit?.({ exitCode: 137 }) + await retry + }) + + it('keeps the Windows force-kill path and fences attachment until physical exit', async () => { + Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' }) + const id = await spawn() + const closing = close(id) + expect(sweep).not.toHaveBeenCalled() + expect(kill).toHaveBeenCalledWith() + await expect(dispatcher.callRequest('pty.attach', { id })).rejects.toThrow('terminating') + exit?.({ exitCode: 137 }) + await closing + }) + + it('keeps graceful shell shutdown off the descendant sweep', async () => { + const id = await spawn() + await dispatcher.callRequest('pty.shutdown', { id, immediate: false }) + expect(sweep).not.toHaveBeenCalled() + expect(kill).toHaveBeenCalledWith('SIGTERM') + }) + it('refuses attach after close completes during source checkpoint wait', async () => { + const id = await spawn() + let finishSource!: (ready: boolean) => void + const sourceWait = new Promise((resolve) => { + finishSource = resolve + }) + const activate = vi.fn(() => false) + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: This stub supplies every publication method exercised by the handler. + handler.setSourcePublication({ + accepts: () => false, + exitPublicationSettled: () => true, + sealAndPublishExit: () => false, + publish: () => false, + onCreditAvailable: () => {}, + receivingActivation: () => undefined, + waitForPendingSend: () => sourceWait, + activate, + getDebugSnapshot: () => ({}), + dispose: () => {} + } as unknown as RelayPtySourcePublication) + const attaching = dispatcher.callRequest('pty.attach', { + id, + sourceRecovery: { + status: 'checkpoint', + deliveryToken: 'token', + ptyIncarnation: 'incarnation', + clientGeneration: 1, + ownerGeneration: 1, + acceptedSourceEndSu: 0 + } + }) + const closing = close(id) + release?.() + await vi.waitFor(() => expect(kill).toHaveBeenCalledTimes(1)) + exit?.({ exitCode: 137 }) + await closing + expect(handler.activePtyCount).toBe(0) + finishSource(true) + await expect(attaching).rejects.toThrow() + expect(activate).not.toHaveBeenCalled() + }) + + it('retains claim if close starts before initial claim liveness validation', async () => { + let closing: Promise | undefined + let closeId = '' + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: This stub supplies every publication method exercised by the handler. + handler.setSourcePublication({ + accepts: () => false, + exitPublicationSettled: () => true, + sealAndPublishExit: () => false, + publish: () => false, + onCreditAvailable: () => {}, + receivingActivation: () => undefined, + waitForPendingSend: async () => true, + activate: (id: string) => { + if (!closeId) { + closeId = id + queueMicrotask(() => { + closing = close(id) + void closing.catch(() => {}) + }) + } + return false + }, + getDebugSnapshot: () => ({}), + dispose: () => {} + } as unknown as RelayPtySourcePublication) + await expect(spawn({ agentSessionEnsure: ensure })).rejects.toThrow('terminating') + expect(handler.activePtyCount).toBe(1) + const firstExit = exit + const retried = spawn({ agentSessionEnsure: ensure }) + const outcome = await retried.then( + () => 'created', + () => 'rejected' + ) + const spawnCount = mockPtySpawn.mock.calls.length + release?.() + firstExit?.({ exitCode: 137 }) + await closing + expect(outcome).toBe('rejected') + expect(spawnCount).toBe(1) + }) +}) diff --git a/src/relay/pty-handler-inventory-process-evidence.test.ts b/src/relay/pty-handler-inventory-process-evidence.test.ts index f0d5b068304..0ab15977f0b 100644 --- a/src/relay/pty-handler-inventory-process-evidence.test.ts +++ b/src/relay/pty-handler-inventory-process-evidence.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' // Regression guard for the SHIPPED inventory path. `pty.listProcesses` resolves // every managed pane's title from one batched host capture; a per-pane tree walk // would restore the O(panes x rows) scan on the relay's single event-loop thread, diff --git a/src/relay/pty-handler-output-drain-differential.test.ts b/src/relay/pty-handler-output-drain-differential.test.ts index cbaee4bcb75..d6c7e92470f 100644 --- a/src/relay/pty-handler-output-drain-differential.test.ts +++ b/src/relay/pty-handler-output-drain-differential.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' const { mockPtySpawn, mockPtyInstance } = vi.hoisted(() => ({ diff --git a/src/relay/pty-handler-output-streaming.test.ts b/src/relay/pty-handler-output-streaming.test.ts index 3be79255fcc..af0a4cbdfaa 100644 --- a/src/relay/pty-handler-output-streaming.test.ts +++ b/src/relay/pty-handler-output-streaming.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' import { PTY_STARTUP_INGRESS_VERSION } from '../shared/pty-startup-ingress' diff --git a/src/relay/pty-handler-ownership-attestation.test.ts b/src/relay/pty-handler-ownership-attestation.test.ts index ee1c144df09..513917ef76c 100644 --- a/src/relay/pty-handler-ownership-attestation.test.ts +++ b/src/relay/pty-handler-ownership-attestation.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' // The host half of #9819: a client may only reap a relay PTY it can prove it created, so the relay // has to say who created each one. The attestation is read from the live consumer grant, never from // a spawn parameter — otherwise it would just echo the caller's claim back at it. diff --git a/src/relay/pty-handler-resize-stale-pty.test.ts b/src/relay/pty-handler-resize-stale-pty.test.ts index 0dcb6b56497..d3478bc6825 100644 --- a/src/relay/pty-handler-resize-stale-pty.test.ts +++ b/src/relay/pty-handler-resize-stale-pty.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' const { mockPtySpawn, mockPtyInstance, mockCreateShellPromptReadinessProbe } = vi.hoisted(() => ({ diff --git a/src/relay/pty-handler-retired-pane-surface.test.ts b/src/relay/pty-handler-retired-pane-surface.test.ts index 959c6f06330..c8f50ebf13a 100644 --- a/src/relay/pty-handler-retired-pane-surface.test.ts +++ b/src/relay/pty-handler-retired-pane-surface.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' const { mockPtySpawn, mockPtyInstance, mockCreateShellPromptReadinessProbe } = vi.hoisted(() => ({ diff --git a/src/relay/pty-handler-revive.test.ts b/src/relay/pty-handler-revive.test.ts index bd0dffbd1b8..88155e38242 100644 --- a/src/relay/pty-handler-revive.test.ts +++ b/src/relay/pty-handler-revive.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' import { existsSync, rmSync } from 'node:fs' import { homedir, tmpdir } from 'node:os' diff --git a/src/relay/pty-handler-shell-resolution.test.ts b/src/relay/pty-handler-shell-resolution.test.ts index 9abd9b90d10..679c2babab7 100644 --- a/src/relay/pty-handler-shell-resolution.test.ts +++ b/src/relay/pty-handler-shell-resolution.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' import * as gitBash from '../main/git-bash' import * as ptyShellUtils from './pty-shell-utils' diff --git a/src/relay/pty-handler-shutdown-signals.test.ts b/src/relay/pty-handler-shutdown-signals.test.ts index 951ce7209e0..5bc3373599c 100644 --- a/src/relay/pty-handler-shutdown-signals.test.ts +++ b/src/relay/pty-handler-shutdown-signals.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' const { mockPtySpawn, mockPtyInstance, mockCreateShellPromptReadinessProbe } = vi.hoisted(() => ({ diff --git a/src/relay/pty-handler-source-publication.test.ts b/src/relay/pty-handler-source-publication.test.ts index 71e8c12b48f..341931cffb5 100644 --- a/src/relay/pty-handler-source-publication.test.ts +++ b/src/relay/pty-handler-source-publication.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { PTY_STARTUP_INGRESS_VERSION } from '../shared/pty-startup-ingress' import { diff --git a/src/relay/pty-handler-spawn-admission.test.ts b/src/relay/pty-handler-spawn-admission.test.ts index 6fef02a9cc0..ea5c6ca3486 100644 --- a/src/relay/pty-handler-spawn-admission.test.ts +++ b/src/relay/pty-handler-spawn-admission.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' diff --git a/src/relay/pty-handler-spawn-cwd.test.ts b/src/relay/pty-handler-spawn-cwd.test.ts index 2aab95a401e..fd2c64849e4 100644 --- a/src/relay/pty-handler-spawn-cwd.test.ts +++ b/src/relay/pty-handler-spawn-cwd.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' import { mkdtempSync, mkdirSync, rmSync } from 'node:fs' import { homedir, tmpdir } from 'node:os' diff --git a/src/relay/pty-handler-spawn-environment.test.ts b/src/relay/pty-handler-spawn-environment.test.ts index 8879686821e..020b1256cc3 100644 --- a/src/relay/pty-handler-spawn-environment.test.ts +++ b/src/relay/pty-handler-spawn-environment.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs' import { homedir, tmpdir } from 'node:os' diff --git a/src/relay/pty-handler-startup-command-delivery.test.ts b/src/relay/pty-handler-startup-command-delivery.test.ts index 9e7c29202c7..2215eff9d76 100644 --- a/src/relay/pty-handler-startup-command-delivery.test.ts +++ b/src/relay/pty-handler-startup-command-delivery.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' diff --git a/src/relay/pty-handler-windows-child-process-evidence.test.ts b/src/relay/pty-handler-windows-child-process-evidence.test.ts index 6d723824a04..75f7382df8d 100644 --- a/src/relay/pty-handler-windows-child-process-evidence.test.ts +++ b/src/relay/pty-handler-windows-child-process-evidence.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' // Regression guard for the Windows SSH child-process answer. The relay used to return a hardcoded // `false` here, which every close guard reads as "nothing is running in this pane" -- so a Windows // SSH pane running a build closed with no prompt. The answer now comes from the process table, and diff --git a/src/relay/pty-handler.ts b/src/relay/pty-handler.ts index 4a55d6b587b..80b0bd62620 100644 --- a/src/relay/pty-handler.ts +++ b/src/relay/pty-handler.ts @@ -1,5 +1,6 @@ /* oxlint-disable max-lines */ import type { IPty } from 'node-pty' +import { killWithDescendantSweep } from '../main/pty-descendant-termination' import type * as NodePty from 'node-pty' import { existsSync } from 'node:fs' import { basename, join } from 'node:path' @@ -236,6 +237,7 @@ type ManagedPty = { * spawn reply to skip waiting for a marker that will never come (fish, sh, Windows). */ shellReadyArmed?: boolean physicalExit?: PhysicalExitTracker + immediateClose?: Promise forceKillSent?: boolean gracefulKillSent?: boolean startupIngress?: PtyStartupIngress @@ -1675,6 +1677,7 @@ export class PtyHandler { const existing = this.agentSessionCreateOperations.get(operationId) if (existing) { const result = await existing + this.assertPtyNotClosing(this.ptys.get(result.id)) this.sourcePublication?.activate(result.id, result.incarnationId, context) const sourceActivation = context && this.sourcePublication?.receivingActivation?.(result.id, context.clientId) @@ -1789,6 +1792,7 @@ export class PtyHandler { this.agentSessionOwners.release(result.owner.ptyId, result.owner.generation) throw new Error('agent_session_exited_during_start') } + this.assertPtyNotClosing(managed) managed.agentSessionOwners = this.agentSessionOwners.listForPty(managed.id) const adoptedReplay = result.disposition === 'adopted' ? managed.buffered.read() : '' this.sourcePublication?.activate(managed.id, managed.incarnationId, context) @@ -2060,6 +2064,8 @@ export class PtyHandler { throw new Error(`PTY "${id}" not found`) } + this.assertPtyNotClosing(managed) + // Why: verify liveness because shells can exit without node-pty onExit. if (this.reapPtyProvenExited(managed)) { // Why the marker: this is the ONLY not-found answer backed by a liveness check. The unmarked @@ -2098,6 +2104,10 @@ export class PtyHandler { ) { sourceRecovery = Object.freeze({ status: 'checkpointUnavailable' }) } + if (this.ptys.get(id) !== managed || managed.disposed) { + throw new Error(`PTY "${id}" not found`) + } + this.assertPtyNotClosing(managed) const activation = this.sourcePublication?.activate( id, managed.incarnationId, @@ -2272,15 +2282,51 @@ export class PtyHandler { if (immediate) { this.releaseStartupCommand(managed) this.flushPtyOutput(id) - this.requestForceKill(managed) - // Why: preserve timed-out entries so onExit/retry owns native handles. - await this.waitForPhysicalExit(managed, IMMEDIATE_PTY_EXIT_TIMEOUT_MS) + await this.closeImmediately(managed) } else { this.releaseStartupCommand(managed) this.requestGracefulKill(managed, 'force-kill') } } + private assertPtyNotClosing(managed: ManagedPty | undefined): void { + if (managed?.immediateClose) { + throw new Error(`PTY "${managed.id}" is terminating`) + } + } + + private async closeImmediately(managed: ManagedPty): Promise { + if (managed.immediateClose) { + return managed.immediateClose + } + const ownsRoot = (): boolean => this.ptys.get(managed.id) === managed && !managed.disposed + const close = async (): Promise => { + if (process.platform === 'win32') { + this.requestForceKill(managed) + } else { + await killWithDescendantSweep( + managed.pty.pid, + () => { + if (ownsRoot()) { + this.requestForceKill(managed) + } + }, + { ownsRoot, terminateOwnedTree: () => terminatePtyJob(managed.pty) } + ) + } + await this.waitForPhysicalExit(managed, IMMEDIATE_PTY_EXIT_TIMEOUT_MS) + } + const pending = close() + managed.immediateClose = pending + try { + await pending + } finally { + if (managed.immediateClose === pending) { + managed.immediateClose = undefined + } + } + } + /** Re-decide, on the host, whether the caller may destroy this PTY. * * `pty.shutdown` is irreversible and its siblings `pty.spawn`/`pty.attach` already take a diff --git a/src/relay/relay-daemon-fatal-reap.test.ts b/src/relay/relay-daemon-fatal-reap.test.ts index 071185fc311..9e94af4c78c 100644 --- a/src/relay/relay-daemon-fatal-reap.test.ts +++ b/src/relay/relay-daemon-fatal-reap.test.ts @@ -1,3 +1,4 @@ +import './mock-descendant-sweep' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { PtyHandler } from './pty-handler' diff --git a/tests/tools/omp-relay-close-lifecycle.md b/tests/tools/omp-relay-close-lifecycle.md new file mode 100644 index 00000000000..d0ef5052ba3 --- /dev/null +++ b/tests/tools/omp-relay-close-lifecycle.md @@ -0,0 +1,67 @@ +# OMP relay-host immediate-close probe (#9530) + +This opt-in probe uses a real installed OMP binary and native PTYs behind production +`PtyHandler` spawn/data/shutdown handlers. The dispatcher is an in-process test +transport; no SSH connection or rendered client is exercised. OMP source is read-only. + +```sh +ORCA_BACKGROUND_LAUNCH=1 ORCA_OMP_PROBE_BINARY=/absolute/path/to/omp \ + ORCA_OMP_PROBE_SHELL=/bin/bash \ + node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts \ + tests/tools/omp-relay-close-lifecycle.test.mjs +``` + +The test defaults to zsh on macOS and bash on Linux. Windows is skipped. It needs +existing native node-pty dependencies; do not install or rebuild as part of the probe. +HOME, user profile, XDG roots and OMP/PI agent roots are disposable, profiles cleared, +and zsh inheritance fenced to the disposable root. No model request is made. The +probe runs `! /bin/sleep 120`, records exact shell/OMP/tool process rows, requests +immediate close, and observes those PIDs independently of the relay inventory. +Matching PID/start-time/group identities bound leftover cleanup. Reports and capped +terminal transcripts stay in `.bench-fixtures/omp-relay-close-*`. + +## Measured on macOS with OMP 18.1.18 + +At source base `93c370246388`, bash mode leaves sleep PID 43156, PGID 43156, alive +and reparented to PID 1 after root PID 42902 and OMP PID 42949 exit. The zsh control +exits cleanly: OMP uses a headless PTY for zsh/fish user-shell tools, while bash +uses its embedded-shell subprocess path. Thus an external command alone does not +determine the process lifetime; the configured user shell matters. + +With the correction, the same bash probe leaves none of its captured PIDs present. +This is detached-tool leakage, not proof of the original foreground-OMP-survives +report. The local-provider/daemon correction is PR #20642; this probe and correction +cover the separate direct-relay backend. + +## Reliability contract + +- Invariant: `terminal-session.explicit-close-retirement`. Explicit immediate close + captures still-parented detached descendants before root termination, preserves + the exact host owner through physical exit, and cannot attach/adopt that owner + while the close is pending. A concurrent close joins the same operation. +- Failure source/oracle: actual OMP external sleep survives the bash-mode relay + close before the fix; independently queried owned PIDs are absent afterward. + Unit tests also cover pending attachment/adoption/create replay, natural exit + during capture, signal failure/retry, retained claims during initial promotion, + and close completing while attachment awaits a source checkpoint. +- Gate: the existing experimental explicit-close gate's descendant/backend tests, + relay lifecycle suites and this opt-in real-PTY probe. Live SSH transport and + rendered client flows remain explicit validation gaps. +- Budget: one existing bounded process-table capture (one-second timeout, 32-MiB + cap), plus one bounded identity recheck after the two-second grace when there + are descendants. Same-turn captures coalesce; no recurring polling is added. +- Authority: the execution host does all process inspection/signaling. Pending-close + refusal carries no proven-exited marker; it is not evidence of process death. + No new RPC fields/opcodes or required capabilities. Older clients receive an + ordinary failed attach while close is pending, not a successful doomed attachment. +- Scope: every immediate POSIX relay close, including still-parented intentionally + detached jobs. Graceful close, disconnect grace, keep-alive and fatal-exit/dispose + policies are unchanged. Windows retains its immediate force-kill path and now + rejects attachment during the physical-exit wait. Folder workspaces and worktrees + use the same PTY identity, without repository metadata checks. +- Gaps: macOS runtime evidence only; Linux/Windows/WSL runtime, live SSH/mobile and + mixed-version clients are not exercised. Children reparented before capture and + same-second identity ambiguity retain the incumbent cleanup limitations. + +Mock-PTY suites isolate the sweep: their fake PIDs often equal the test runner's PID +and must never reach the real host process table or descendant signals. diff --git a/tests/tools/omp-relay-close-lifecycle.test.mjs b/tests/tools/omp-relay-close-lifecycle.test.mjs new file mode 100644 index 00000000000..2730356ebd1 --- /dev/null +++ b/tests/tools/omp-relay-close-lifecycle.test.mjs @@ -0,0 +1,150 @@ +import { it, expect } from 'vitest' +import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs' +import { join } from 'node:path' +import { tmpdir } from 'node:os' +import { + createMockDispatcher, + createTestPtyHandler +} from '../../src/relay/pty-handler-test-harness.ts' +import { + captureDescendantSnapshot, + readProcessTable +} from '../../src/main/pty-descendant-termination.ts' +import { runProcess } from '../../src/shared/child-process/run-process.ts' + +const binary = process.env.ORCA_OMP_PROBE_BINARY +const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) +const quote = (value) => `'${value.replaceAll("'", "'\\''")}'` + +it.skipIf(!binary || process.platform === 'win32')( + 'closes actual OMP detached tools through the relay host', + async () => { + const fixtures = join(process.cwd(), '.bench-fixtures') + mkdirSync(fixtures, { recursive: true }) + const output = mkdtempSync(join(fixtures, 'omp-relay-close-')) + const home = mkdtempSync(join(tmpdir(), 'orca-omp-relay-close-home-')) + const agentHome = join(home, 'agent') + mkdirSync(agentHome) + const config = join(home, 'probe.yml') + writeFileSync( + config, + 'startup:\n setupWizard: false\n showSplash: false\n checkUpdate: false\n' + ) + const dispatcher = createMockDispatcher() + let transcript = '' + dispatcher.notify = (method, params) => { + if (method === 'pty.data' && typeof params?.data === 'string') { + transcript = (transcript + params.data).slice(-131072) + } + } + const handler = createTestPtyHandler(dispatcher) + let snapshot + let id + try { + const spawned = await dispatcher.callRequest('pty.spawn', { + cwd: home, + cols: 120, + rows: 35, + env: { + HOME: home, + USERPROFILE: home, + ZDOTDIR: home, + ORCA_ORIG_ZDOTDIR: home, + SHELL: + process.env.ORCA_OMP_PROBE_SHELL ?? + (process.platform === 'darwin' ? '/bin/zsh' : '/bin/bash'), + XDG_CONFIG_HOME: join(home, 'config'), + XDG_DATA_HOME: join(home, 'data'), + XDG_CACHE_HOME: join(home, 'cache'), + XDG_STATE_HOME: join(home, 'state'), + OMP_CODING_AGENT_DIR: agentHome, + PI_CODING_AGENT_DIR: agentHome, + OMP_PROFILE: '', + PI_PROFILE: '', + PI_CONFIG_DIR: '.omp', + PI_CONFIG_FILES: '', + ORCA_BACKGROUND_LAUNCH: '1', + ORCA_PANE_KEY: 'omp-relay-probe:owned-leaf', + ORCA_TAB_ID: 'omp-relay-probe' + }, + envToDelete: ['BASH_ENV', 'ENV', 'ORCA_OMP_STATUS_EXTENSION', 'ORCA_PI_STATUS_EXTENSION'] + }) + id = spawned.id + const [entry] = JSON.parse(await dispatcher.callRequest('pty.serialize', { ids: [id] })) + snapshot = await captureDescendantSnapshot(entry.pid) + expect(snapshot?.root?.pid).toBe(entry.pid) + dispatcher.callNotification('pty.data', { + id, + data: `${quote(binary)} --no-session --config ${quote(config)}\r` + }) + await pause(5000) + dispatcher.callNotification('pty.data', { id, data: '! /bin/sleep 120\r' }) + for (let attempt = 0; attempt < 25; attempt++) { + await pause(200) + snapshot = await captureDescendantSnapshot(entry.pid) + if (snapshot?.descendants.length > 1) { + break + } + } + expect(snapshot?.descendants.length).toBeGreaterThan(1) + const pids = [entry.pid, ...snapshot.descendants.map((row) => row.pid)] + const rows = async () => { + const result = await runProcess({ + program: 'ps', + args: ['-p', pids.join(','), '-o', 'pid=,ppid=,pgid=,stat=,comm='], + maxOutputBytes: 16000 + }) + expect(result.timedOut).toBe(false) + expect(result.signal).toBeNull() + expect(result.stderr.trim()).toBe('') + expect([0, 1]).toContain(result.code) + if (result.code === 1) { + expect(result.stdout.trim()).toBe('') + } + return result.stdout.trim() + } + const before = await rows() + expect(before).toContain('omp') + expect(before).toContain('sleep') + await dispatcher.callRequest('pty.shutdown', { + id, + immediate: true, + expectedIncarnationId: spawned.incarnationId + }) + await pause(6000) + const after = await rows() + writeFileSync( + join(output, 'report.json'), + JSON.stringify({ backend: 'relay-host', before, after, pid: entry.pid, id, home }, null, 2) + ) + writeFileSync(join(output, 'transcript.txt'), transcript) + console.log(output) + expect(after).toBe('') + } finally { + if (snapshot) { + const current = await readProcessTable() + const owned = [ + ...snapshot.descendants, + ...(snapshot.root ? [{ ...snapshot.root, pgid: snapshot.rootPgid }] : []) + ] + for (const row of current.rows) { + if ( + owned.some( + (known) => + known.pid === row.pid && + known.startedAt === row.startedAt && + known.pgid === row.pgid + ) + ) { + try { + process.kill(row.pid, 'SIGKILL') + } catch {} + } + } + } + await handler.dispose({ waitForPhysicalExit: false }) + rmSync(home, { recursive: true, force: true }) + } + }, + 45000 +) From 3de77340fc99de53aeb980bbd3c8cb01917bf87d Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Fri, 18 Sep 2026 00:11:33 -0400 Subject: [PATCH 079/168] fix: apply managed Claude auth to Agent Teams (#21356) * fix: apply managed Claude auth to agent teams * test: update agent teams auth launch expectation * refactor: derive agent teams auth deletions --- src/cli/handlers/core.test.ts | 46 +++++++++++++++++++ src/cli/handlers/core.ts | 20 ++++---- ...index-worktree-selector-resolution.test.ts | 1 + ...resolve-terminal-split-source-authority.ts | 20 ++++++-- src/main/runtime/orca-runtime-state-fields.ts | 5 ++ .../terminal/terminal-lifecycle-methods.ts | 3 +- .../startup/main-process-runtime-service.ts | 1 + .../rpc-contract/terminal-unary-params.ts | 3 +- 8 files changed, 86 insertions(+), 13 deletions(-) diff --git a/src/cli/handlers/core.test.ts b/src/cli/handlers/core.test.ts index ac5444566d9..ba2daf90c30 100644 --- a/src/cli/handlers/core.test.ts +++ b/src/cli/handlers/core.test.ts @@ -130,4 +130,50 @@ describe('orca claude-teams CLI handler', () => { expect(spawnEnv.PATH).toBe('/shim:/usr/bin') } ) + + it.skipIf(isWindows)('removes managed auth variables before spawning Claude', async () => { + const previousApiKey = process.env.ANTHROPIC_API_KEY + process.env.ANTHROPIC_API_KEY = 'sk-ant-inherited' + callMock.mockResolvedValueOnce({ + result: { + launch: { + env: { + CLAUDE_CONFIG_DIR: '/managed/claude', + CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: '1' + }, + envToDelete: ['ANTHROPIC_API_KEY'] + } + } + }) + try { + await runClaudeTeams() + } finally { + if (previousApiKey === undefined) { + delete process.env.ANTHROPIC_API_KEY + } else { + process.env.ANTHROPIC_API_KEY = previousApiKey + } + } + + const spawnEnv = spawnMock.mock.calls.at(-1)?.[2].env as SpawnEnv + expect(spawnEnv.ANTHROPIC_API_KEY).toBeUndefined() + expect(spawnEnv.CLAUDE_CONFIG_DIR).toBe('/managed/claude') + }) + + it.skipIf(isWindows)('preserves API-key auth when no managed deletion is requested', async () => { + const previousApiKey = process.env.ANTHROPIC_API_KEY + process.env.ANTHROPIC_API_KEY = 'sk-ant-system' + try { + await runClaudeTeams() + } finally { + if (previousApiKey === undefined) { + delete process.env.ANTHROPIC_API_KEY + } else { + process.env.ANTHROPIC_API_KEY = previousApiKey + } + } + + const spawnEnv = spawnMock.mock.calls.at(-1)?.[2].env as SpawnEnv + expect(spawnEnv.ANTHROPIC_API_KEY).toBe('sk-ant-system') + }) }) diff --git a/src/cli/handlers/core.ts b/src/cli/handlers/core.ts index d4979ff2ae9..6a1b7ab3918 100644 --- a/src/cli/handlers/core.ts +++ b/src/cli/handlers/core.ts @@ -73,16 +73,20 @@ export const CORE_HANDLERS: Record = { 'orca claude-teams must be run inside an Orca terminal.' ) } - const response = await client.call<{ launch: { env: Record } }>( - 'agentTeams.prepareLaunch', - { - paneKey, - env: envRecord() - } - ) + const inheritedEnv = envRecord() + const response = await client.call<{ + launch: { env: Record; envToDelete?: string[] } + }>('agentTeams.prepareLaunch', { + paneKey, + env: inheritedEnv, + prepareAuth: true + }) + for (const key of response.result.launch.envToDelete ?? []) { + delete inheritedEnv[key] + } process.exitCode = await runClaudeAgentTeams( { - ...envRecord(), + ...inheritedEnv, ...response.result.launch.env }, rawArgs ?? [] diff --git a/src/cli/index-worktree-selector-resolution.test.ts b/src/cli/index-worktree-selector-resolution.test.ts index 70c5ca57ad5..c3f3d7dbd60 100644 --- a/src/cli/index-worktree-selector-resolution.test.ts +++ b/src/cli/index-worktree-selector-resolution.test.ts @@ -153,6 +153,7 @@ describe('orca cli worktree awareness', () => { expect(callMock).toHaveBeenCalledWith('agentTeams.prepareLaunch', { paneKey: 'tab-1:11111111-1111-4111-8111-111111111111', + prepareAuth: true, env: expect.objectContaining({ ORCA_PANE_KEY: 'tab-1:11111111-1111-4111-8111-111111111111' }) diff --git a/src/main/runtime/orca-runtime-resolve-terminal-split-source-authority.ts b/src/main/runtime/orca-runtime-resolve-terminal-split-source-authority.ts index 4723e186039..49d55d96c17 100644 --- a/src/main/runtime/orca-runtime-resolve-terminal-split-source-authority.ts +++ b/src/main/runtime/orca-runtime-resolve-terminal-split-source-authority.ts @@ -14,6 +14,7 @@ import { ensureClaudeAgentTeamsShimDir, resolveClaudeAgentTeamsShimBin } from './claude-agent-teams-shim-env' +import { applyClaudeEnvPatch } from '../claude-accounts/environment' export class OrcaRuntimeWithResolveTerminalSplitSourceAuthority extends OrcaRuntimeWithSplitPtyBackedTerminal { protected resolveTerminalSplitSourceAuthority( @@ -109,6 +110,7 @@ export class OrcaRuntimeWithResolveTerminalSplitSourceAuthority extends OrcaRunt async prepareClaudeAgentTeamsLeader(args: { paneKey: string baseEnv?: Record + prepareAuth?: boolean }): Promise<{ env: Record }> { const handle = this.getTerminalHandleForPaneKey(args.paneKey) if (!handle) { @@ -116,26 +118,38 @@ export class OrcaRuntimeWithResolveTerminalSplitSourceAuthority extends OrcaRunt } return await this.prepareClaudeAgentTeamsLeaderForHandle({ handle, - baseEnv: args.baseEnv + baseEnv: args.baseEnv, + prepareAuth: args.prepareAuth }) } async prepareClaudeAgentTeamsLeaderForHandle(args: { handle: string baseEnv?: Record - }): Promise<{ env: Record }> { + prepareAuth?: boolean + }): Promise<{ env: Record; envToDelete?: string[] }> { const baseEnv = { ...process.env, ...args.baseEnv } + const inheritedEnvKeys = new Set(Object.keys(baseEnv)) + const auth = args.prepareAuth && this.prepareClaudeAuth ? await this.prepareClaudeAuth() : null + if (auth) { + applyClaudeEnvPatch(baseEnv, auth.envPatch, { stripAuthEnv: auth.stripAuthEnv }) + } + const envToDelete = auth?.stripAuthEnv + ? [...inheritedEnvKeys].filter((key) => !(key in baseEnv)) + : undefined const shimDir = await ensureClaudeAgentTeamsShimDir() const shimBin = resolveClaudeAgentTeamsShimBin(baseEnv) - return this.claudeAgentTeams.createLaunchEnv({ + const launch = this.claudeAgentTeams.createLaunchEnv({ leaderHandle: args.handle, baseEnv, shimDir, shimBin }) + const env = auth ? { ...auth.envPatch, ...launch.env } : launch.env + return envToDelete ? { env, envToDelete } : { env } } // Why: a leader handle that never binds to a PTY (lost pane race) has no exit diff --git a/src/main/runtime/orca-runtime-state-fields.ts b/src/main/runtime/orca-runtime-state-fields.ts index 781f6075be1..a28ead15724 100644 --- a/src/main/runtime/orca-runtime-state-fields.ts +++ b/src/main/runtime/orca-runtime-state-fields.ts @@ -3,6 +3,7 @@ import { OrcaRuntimeWithLinearCommands } from './orca-runtime-linear-commands' import type { RuntimeStore } from './runtime-store-contract' import type { StatsCollector } from '../stats/collector' import type { IPtyProvider } from '../providers/types' +import type { PrepareClaudeAuth } from '../ipc/pty/host-env/types' import type { RuntimeTerminalAgentStatusEvent } from './runtime-terminal-contracts' import type { TerminalSideEffectBatch } from '../../shared/terminal-side-effect-facts' import type { AgentStatusIpcPayload } from '../../shared/agent-status-types' @@ -41,12 +42,15 @@ import { registerConptyDa1OverrideInstaller } from './terminal-model-query-autho import { registerTerminalViewAttributesApplier } from './terminal-view-attribute-store' export class OrcaRuntimeWithStateFields extends OrcaRuntimeWithLinearCommands { + protected readonly prepareClaudeAuth?: PrepareClaudeAuth + constructor( store: RuntimeStore | null = null, stats?: StatsCollector, deps?: { getLocalProvider?: () => IPtyProvider getSshProvider?: (connectionId: string) => IPtyProvider | undefined + prepareClaudeAuth?: PrepareClaudeAuth onPtyStopped?: (ptyId: string) => void onTerminalAgentStatus?: (event: RuntimeTerminalAgentStatusEvent) => void onTerminalSideEffects?: (batch: TerminalSideEffectBatch) => void @@ -103,6 +107,7 @@ export class OrcaRuntimeWithStateFields extends OrcaRuntimeWithLinearCommands { ) { super() this.store = store + this.prepareClaudeAuth = deps?.prepareClaudeAuth store?.onSettingsChanged?.((updates) => { if ('experimentalStructuredNativeChat' in updates) { this.notifyMobileSessionTabsChanged() diff --git a/src/main/runtime/rpc/methods/terminal/terminal-lifecycle-methods.ts b/src/main/runtime/rpc/methods/terminal/terminal-lifecycle-methods.ts index 2fcdc2bc92c..58e1d5d825c 100644 --- a/src/main/runtime/rpc/methods/terminal/terminal-lifecycle-methods.ts +++ b/src/main/runtime/rpc/methods/terminal/terminal-lifecycle-methods.ts @@ -189,7 +189,8 @@ export const TERMINAL_LIFECYCLE_METHODS = [ handler: async (params, { runtime }) => ({ launch: await runtime.prepareClaudeAgentTeamsLeader({ paneKey: params.paneKey, - baseEnv: params.env + baseEnv: params.env, + prepareAuth: params.prepareAuth }) }) }) diff --git a/src/main/startup/main-process-runtime-service.ts b/src/main/startup/main-process-runtime-service.ts index 62716bbcdec..35d008d1b40 100644 --- a/src/main/startup/main-process-runtime-service.ts +++ b/src/main/startup/main-process-runtime-service.ts @@ -72,6 +72,7 @@ export function initializeMainProcessRuntime(): OrcaRuntimeService { // `orca serve`, which never opens one, and the fleet path runs there too. const observedPaneIdentities = new AgentStatusObservedPaneIdentities() const runtime = new OrcaRuntimeService(store, stats, { + prepareClaudeAuth: (target) => state.claudeRuntimeAuth!.prepareForClaudeLaunch(target), agentSessionClaimSigner: loadAgentSessionClaimSigner( getProfileUserDataPath(), getProfileUserDataPath() diff --git a/src/shared/rpc-contract/terminal-unary-params.ts b/src/shared/rpc-contract/terminal-unary-params.ts index 9b735952096..ae34af7ebd9 100644 --- a/src/shared/rpc-contract/terminal-unary-params.ts +++ b/src/shared/rpc-contract/terminal-unary-params.ts @@ -234,5 +234,6 @@ export const AgentTeamsTmuxCompat = z.object({ export const AgentTeamsPrepareLaunch = z.object({ paneKey: requiredString('Missing pane key'), - env: z.record(z.string(), z.string()).optional() + env: z.record(z.string(), z.string()).optional(), + prepareAuth: z.boolean().optional() }) From a84f16df3d794a5a55d579566715600084798cc1 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Fri, 18 Sep 2026 00:12:36 -0400 Subject: [PATCH 080/168] fix(mobile): mint one pairing offer per Continue on the sidebar page (#21261) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(mobile): mint one pairing offer per Continue on the sidebar page Step 2 auto-minted as soon as it became visible, which is the same commit that starts the network-interface lookup. The offer therefore advertised whatever address was left over from the last visit (or none at all, so main picked its own default), and when the lookup settled on a different address the refresh handler reminted with rotate: true. Two overlapping getPairingQR calls then raced for one pending credential: main rotates the pending device away for the rotate mint, and orders concurrent offers by arrival at its generation counter rather than by the order the renderer issued them, so the request the pane is waiting on can be the one main decided to supersede. Defer the auto-mint until the interface lookup settles, and keep Step 2 reading as busy while it waits — the sidebar has no separate Generate step the user is expected to reach, so it must still mint on its own, unlike Settings which clears and waits for an explicit press. * fix(mobile): gate the Step 2 mint on this flow visit's address lookup The first attempt gated on a single boolean ref meaning "an address lookup is running". That cannot describe a re-entrant operation: entering the flow, leaving, and re-entering runs two overlapping lookups, and the first to land clears the flag while the second is still out — so the mint went out against the superseded lookup's address and the second lookup then reminted with rotate: true. The same double mint the change exists to remove, one path over. Gate on positive evidence instead. Each flow entry bumps a visit counter; the lookup records the visit it answered (max, so an abandoned visit landing last cannot walk the marker backwards); the mint waits for addressedFlowVisit === pairingFlowVisit, which is false at t=0 by construction and makes exactly one false-to-true transition per visit. The ref is gone and the effect's dependencies now name what it depends on. A superseded lookup's response is also discarded outright, so it cannot move the picker onto an address a newer lookup already replaced — that reselection is itself a remint trigger. The derived busy flag collapses to one clause and is renamed awaitingPairingAddress: it was being passed down as pairLoading while local readers used the real one. It stays separate from pairLoading because that feeds shouldRegenerate in the invalidation hook, where merging them would let a mode switch mint before the address settles. * fix(mobile): put the visit-settled write behind the lookup epoch guard setAddressedFlowVisit was the one completion side-effect outside networkInterfacesRequestIdRef, so a superseded lookup *for the same visit* still marked that visit addressed and released the mint while its own replacement was still pending — the newer address then rotated the offer away. The visit counter cannot see this case: both lookups belong to one visit, and only the request epoch distinguishes them. Reaching it needs a manual Refresh click to beat the commit that disables that button, so field impact is low. The point is that the invariant is now structural instead of resting on a button being disabled in time. Math.max is dropped with the move. Every visit bump starts its own lookup, so the newest request always carries the highest visit and the marker cannot move backwards — the max could no longer be killed by any single mutation, which made it dead code asserting a hazard the guard removes. Also swap the test reset to _resetPairedMobileDevicesCacheForTests, matching the sibling suites: replacePairedMobileDevices is production API that publishes loaded:true and leaves the recovery-listener refcount untouched. * refactor(mobile): make the unaddressed flow visit an explicit null -1 only worked because visits start at 0 and count up; null says "no visit has been addressed yet" without depending on that. Also record at the visit bump why it cannot move into the stage effect: an effect runs a render after Step 2 is visible, so the auto-mint would see the previous visit settled. * fix(mobile): invalidate abandoned pairing mints --- .../src/components/mobile/MobilePage.test.tsx | 352 ++++++++++++++++++ .../src/components/mobile/MobilePage.tsx | 58 ++- 2 files changed, 398 insertions(+), 12 deletions(-) diff --git a/src/renderer/src/components/mobile/MobilePage.test.tsx b/src/renderer/src/components/mobile/MobilePage.test.tsx index 2149cadc284..5805bca46e6 100644 --- a/src/renderer/src/components/mobile/MobilePage.test.tsx +++ b/src/renderer/src/components/mobile/MobilePage.test.tsx @@ -55,7 +55,10 @@ vi.mock('./MobilePageContent', () => ({ onCustomAddressSelect: (address: string) => void onCustomAddressRemove: (address: string) => void beforeCustomAddressChange: (address: string) => Promise + handleBack: () => void handleContinue: () => void + pairAnotherDevice: () => void + pairLoading: boolean pairQrDataUrl: string | null pairQrSize: number | null pairingUrl: string | null @@ -74,6 +77,7 @@ vi.mock('./MobilePageContent', () => ({ {props.stepIdx} {props.connectionMode} {String(props.canGeneratePairing)} + {String(props.pairLoading)} {props.pairQrDataUrl ?? 'none'} {props.pairQrSize ?? 'none'} {props.pairingUrl ?? 'none'} @@ -89,6 +93,12 @@ vi.mock('./MobilePageContent', () => ({ + + @@ -130,6 +140,7 @@ vi.mock('./MobilePageContent', () => ({ })) import MobilePage from './MobilePage' +import { _resetPairedMobileDevicesCacheForTests } from './paired-mobile-devices' describe('MobilePage pairing connection mode', () => { const getPairingQR = vi.fn() @@ -143,6 +154,9 @@ describe('MobilePage pairing connection mode', () => { pairingUrl: 'orca://pair#automatic' }) listNetworkInterfaces.mockReset().mockResolvedValue({ interfaces: [] }) + // The paired-device cache is module state shared by every surface; reset it so + // one test's phones cannot decide the next test's opening stage. + _resetPairedMobileDevicesCacheForTests() mocks.storeState = { closeMobilePage: vi.fn(), orcaProfileAuthStatus: { state: 'connected' }, @@ -540,6 +554,344 @@ describe('MobilePage pairing connection mode', () => { ) }) + it('mints one offer when Continue lands while the address refresh is in flight', async () => { + listNetworkInterfaces.mockResolvedValue({ + interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] + }) + const user = userEvent.setup() + render() + await waitFor(() => expect(screen.getByTestId('stage')).toHaveTextContent('intro')) + await user.click(screen.getByRole('button', { name: 'Enter flow' })) + await user.click(screen.getByRole('button', { name: 'Continue' })) + await waitFor(() => + expect(getPairingQR).toHaveBeenCalledWith({ + address: '10.0.0.5', + connectionMode: 'automatic' + }) + ) + + // Leave the flow so re-entering refetches the interface list, and hold that + // refetch open so Continue is clicked while the address is still unsettled. + await user.click(screen.getByRole('button', { name: 'Back' })) + await user.click(screen.getByRole('button', { name: 'Back' })) + await waitFor(() => expect(screen.getByTestId('stage')).toHaveTextContent('intro')) + getPairingQR.mockClear() + let resolveRefresh: ((value: Record) => void) | undefined + listNetworkInterfaces.mockImplementationOnce( + () => + new Promise((resolve) => { + resolveRefresh = resolve + }) + ) + await user.click(screen.getByRole('button', { name: 'Enter flow' })) + await user.click(screen.getByRole('button', { name: 'Continue' })) + + // Nothing may be minted yet, and Step 2 must read as busy rather than + // offering "Generate a pairing code" it is about to run itself. + expect(getPairingQR).not.toHaveBeenCalled() + expect(screen.getByTestId('pair-loading')).toHaveTextContent('true') + + // The lease moved while the page was away. + resolveRefresh?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.9' }] }) + await waitFor(() => + expect(screen.getByTestId('selected-address')).toHaveTextContent('10.0.0.9') + ) + // One Continue is one offer. Minting against the stale address and then + // rotating to the settled one runs two overlapping mints through main, whose + // rotate deletes the pending credential the first mint already returned. + await waitFor(() => expect(getPairingQR).toHaveBeenCalledTimes(1)) + expect(getPairingQR).toHaveBeenCalledWith({ + address: '10.0.0.9', + connectionMode: 'automatic' + }) + }) + + it('does not let an abandoned mint populate a new pairing visit', async () => { + const user = userEvent.setup() + let resolveAbandonedMint: ((value: Record) => void) | undefined, + resolveCurrentMint: ((value: Record) => void) | undefined + getPairingQR + .mockImplementationOnce(() => new Promise((resolve) => (resolveAbandonedMint = resolve))) + .mockImplementationOnce(() => new Promise((resolve) => (resolveCurrentMint = resolve))) + await openPairingStep() + + await user.click(screen.getByRole('button', { name: 'Back' })) + await user.click(screen.getByRole('button', { name: 'Back' })) + await user.click(screen.getByRole('button', { name: 'Enter flow' })) + await user.click(screen.getByRole('button', { name: 'Continue' })) + await waitFor(() => expect(getPairingQR).toHaveBeenCalledTimes(2)) + + resolveAbandonedMint?.({ available: true, qrDataUrl: 'abandoned' }) + + resolveCurrentMint?.({ available: true, qrDataUrl: 'current' }) + await waitFor(() => expect(screen.getByTestId('pairing-qr')).toHaveTextContent('current')) + }) + + it('mints "Pair another device" against the resolved address, not the default', async () => { + window.api.mobile.listDevices = vi.fn().mockResolvedValue({ + devices: [{ deviceId: 'phone-1', name: 'Pixel', pairedAt: 1, lastSeenAt: 2 }] + }) + listNetworkInterfaces.mockResolvedValue({ + interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] + }) + const user = userEvent.setup() + render() + await waitFor(() => expect(screen.getByTestId('stage')).toHaveTextContent('paired')) + + // This jumps straight to Step 2 in the same commit that starts the interface + // lookup, so the auto-mint always runs before any address is known. + await user.click(screen.getByRole('button', { name: 'Pair another device' })) + + await waitFor(() => + expect(screen.getByTestId('selected-address')).toHaveTextContent('10.0.0.5') + ) + await waitFor(() => expect(getPairingQR).toHaveBeenCalledTimes(1)) + expect(getPairingQR).toHaveBeenCalledWith({ + address: '10.0.0.5', + connectionMode: 'automatic' + }) + + // Returning to the paired list and pairing again is a fresh visit: it must + // wait for its own lookup, not inherit the previous visit's answer. + await user.click(screen.getByRole('button', { name: 'Back' })) + await user.click(screen.getByRole('button', { name: 'Back' })) + await waitFor(() => expect(screen.getByTestId('stage')).toHaveTextContent('paired')) + getPairingQR.mockClear() + let resolveSecondLookup: ((value: Record) => void) | undefined + listNetworkInterfaces.mockImplementationOnce( + () => + new Promise((resolve) => { + resolveSecondLookup = resolve + }) + ) + await user.click(screen.getByRole('button', { name: 'Pair another device' })) + expect(getPairingQR).not.toHaveBeenCalled() + + resolveSecondLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.9' }] }) + await waitFor(() => + expect(screen.getByTestId('selected-address')).toHaveTextContent('10.0.0.9') + ) + await waitFor(() => expect(getPairingQR).toHaveBeenCalledTimes(1)) + expect(getPairingQR).toHaveBeenCalledWith({ + address: '10.0.0.9', + connectionMode: 'automatic' + }) + }) + + it('waits for the newest lookup when the flow is re-entered mid-refresh', async () => { + const user = userEvent.setup() + let resolveFirstLookup: ((value: Record) => void) | undefined + let resolveSecondLookup: ((value: Record) => void) | undefined + listNetworkInterfaces + .mockImplementationOnce( + () => + new Promise((resolve) => { + resolveFirstLookup = resolve + }) + ) + .mockImplementationOnce( + () => + new Promise((resolve) => { + resolveSecondLookup = resolve + }) + ) + render() + await waitFor(() => expect(screen.getByTestId('stage')).toHaveTextContent('intro')) + + // Enter, leave, and re-enter while the first lookup is still unanswered, so + // two lookups overlap and the older one is the first to settle. + await user.click(screen.getByRole('button', { name: 'Enter flow' })) + await user.click(screen.getByRole('button', { name: 'Back' })) + await waitFor(() => expect(screen.getByTestId('stage')).toHaveTextContent('intro')) + await user.click(screen.getByRole('button', { name: 'Enter flow' })) + await user.click(screen.getByRole('button', { name: 'Continue' })) + await waitFor(() => expect(listNetworkInterfaces).toHaveBeenCalledTimes(2)) + + // The superseded lookup answers first. It must neither move the picker nor + // release the mint — this visit's lookup has not answered yet. + resolveFirstLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] }) + await waitFor(() => expect(screen.getByTestId('pair-loading')).toHaveTextContent('true')) + expect(getPairingQR).not.toHaveBeenCalled() + expect(screen.getByTestId('selected-address')).toHaveTextContent('none') + + resolveSecondLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.9' }] }) + await waitFor(() => + expect(screen.getByTestId('selected-address')).toHaveTextContent('10.0.0.9') + ) + await waitFor(() => expect(getPairingQR).toHaveBeenCalledTimes(1)) + expect(getPairingQR).toHaveBeenCalledWith({ + address: '10.0.0.9', + connectionMode: 'automatic' + }) + }) + + it('does not release the mint when a superseded lookup for the same visit settles', async () => { + const user = userEvent.setup() + let resolveEntryLookup: ((value: Record) => void) | undefined + let resolveManualLookup: ((value: Record) => void) | undefined + listNetworkInterfaces + .mockImplementationOnce( + () => + new Promise((resolve) => { + resolveEntryLookup = resolve + }) + ) + .mockImplementationOnce( + () => + new Promise((resolve) => { + resolveManualLookup = resolve + }) + ) + render() + await waitFor(() => expect(screen.getByTestId('stage')).toHaveTextContent('intro')) + await user.click(screen.getByRole('button', { name: 'Enter flow' })) + await user.click(screen.getByRole('button', { name: 'Continue' })) + + // A manual refresh overlaps the entry lookup, so both belong to this visit — + // the visit counter cannot tell them apart, only the request epoch can. + await user.click(screen.getByRole('button', { name: 'Refresh addresses' })) + await waitFor(() => expect(listNetworkInterfaces).toHaveBeenCalledTimes(2)) + + // The superseded lookup answers first. Marking the visit addressed here would + // release the mint against an address its own replacement is about to change. + resolveEntryLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] }) + await waitFor(() => expect(screen.getByTestId('pair-loading')).toHaveTextContent('true')) + expect(getPairingQR).not.toHaveBeenCalled() + expect(screen.getByTestId('selected-address')).toHaveTextContent('none') + + resolveManualLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.9' }] }) + await waitFor(() => + expect(screen.getByTestId('selected-address')).toHaveTextContent('10.0.0.9') + ) + await waitFor(() => expect(getPairingQR).toHaveBeenCalledTimes(1)) + expect(getPairingQR).toHaveBeenCalledWith({ + address: '10.0.0.9', + connectionMode: 'automatic' + }) + }) + + it('does not re-block Step 2 when an abandoned visit’s lookup settles last', async () => { + const user = userEvent.setup() + let resolveAbandonedLookup: ((value: Record) => void) | undefined + let resolveCurrentLookup: ((value: Record) => void) | undefined + listNetworkInterfaces + .mockImplementationOnce( + () => + new Promise((resolve) => { + resolveAbandonedLookup = resolve + }) + ) + .mockImplementationOnce( + () => + new Promise((resolve) => { + resolveCurrentLookup = resolve + }) + ) + render() + await waitFor(() => expect(screen.getByTestId('stage')).toHaveTextContent('intro')) + await user.click(screen.getByRole('button', { name: 'Enter flow' })) + await user.click(screen.getByRole('button', { name: 'Back' })) + await waitFor(() => expect(screen.getByTestId('stage')).toHaveTextContent('intro')) + await user.click(screen.getByRole('button', { name: 'Enter flow' })) + await user.click(screen.getByRole('button', { name: 'Continue' })) + await waitFor(() => expect(listNetworkInterfaces).toHaveBeenCalledTimes(2)) + + resolveCurrentLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.9' }] }) + await waitFor(() => expect(getPairingQR).toHaveBeenCalledTimes(1)) + await waitFor(() => expect(screen.getByTestId('pair-loading')).toHaveTextContent('false')) + + // The abandoned visit answers last. Recording it as the settled visit would + // walk the marker backwards and leave Step 2 waiting on a lookup that is + // never coming, with its Generate action disabled. + resolveAbandonedLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] }) + await waitFor(() => + expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false') + ) + expect(screen.getByTestId('pair-loading')).toHaveTextContent('false') + expect(screen.getByTestId('selected-address')).toHaveTextContent('10.0.0.9') + expect(getPairingQR).toHaveBeenCalledTimes(1) + }) + + it('ignores an interface lookup that settles after a newer one', async () => { + listNetworkInterfaces.mockResolvedValue({ + interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] + }) + const user = userEvent.setup() + await openPairingStep() + await waitFor(() => expect(getPairingQR).toHaveBeenCalledTimes(1)) + + // Two manual refreshes overlap; the older one answers last with a stale list. + let resolveStaleRefresh: ((value: Record) => void) | undefined + listNetworkInterfaces.mockImplementationOnce( + () => + new Promise((resolve) => { + resolveStaleRefresh = resolve + }) + ) + await user.click(screen.getByRole('button', { name: 'Refresh addresses' })) + listNetworkInterfaces.mockResolvedValueOnce({ + interfaces: [{ name: 'Wi-Fi', address: '10.0.0.7' }] + }) + await user.click(screen.getByRole('button', { name: 'Refresh addresses' })) + await waitFor(() => + expect(screen.getByTestId('selected-address')).toHaveTextContent('10.0.0.7') + ) + await waitFor(() => expect(getPairingQR).toHaveBeenCalledTimes(2)) + expect(getPairingQR).toHaveBeenLastCalledWith({ + address: '10.0.0.7', + connectionMode: 'automatic', + rotate: true + }) + + resolveStaleRefresh?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.1' }] }) + await waitFor(() => + expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false') + ) + + // The stale list must not reselect an address and rotate the live offer away. + expect(screen.getByTestId('selected-address')).toHaveTextContent('10.0.0.7') + expect(getPairingQR).toHaveBeenCalledTimes(2) + }) + + it('does not report the pairing step as busy during a manual address refresh', async () => { + listNetworkInterfaces.mockResolvedValue({ + interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] + }) + // Leave Step 2 with no QR on screen: that is the state where a refresh could + // be mistaken for a mint in progress. + getPairingQR.mockResolvedValueOnce({ + available: false, + reason: 'websocket_unavailable', + guidance: 'WebSocket transport is not running' + }) + const user = userEvent.setup() + await openPairingStep() + await waitFor(() => expect(getPairingQR).toHaveBeenCalledTimes(1)) + await waitFor(() => expect(screen.getByTestId('pair-loading')).toHaveTextContent('false')) + expect(screen.getByTestId('pairing-qr')).toHaveTextContent('none') + expect(screen.getByTestId('relay-failure')).toHaveTextContent('none') + + let resolveRefresh: ((value: Record) => void) | undefined + listNetworkInterfaces.mockImplementationOnce( + () => + new Promise((resolve) => { + resolveRefresh = resolve + }) + ) + await user.click(screen.getByRole('button', { name: 'Refresh addresses' })) + await waitFor(() => + expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('true') + ) + + // Nothing is minting, so Step 2 must not claim it is — that would disable the + // Generate action while the user is only re-reading the interface list. + expect(screen.getByTestId('pair-loading')).toHaveTextContent('false') + resolveRefresh?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] }) + await waitFor(() => + expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false') + ) + }) + it('keeps custom intent when the saved address is also discovered', async () => { mocks.storeState.settings = { showMobileButton: true, diff --git a/src/renderer/src/components/mobile/MobilePage.tsx b/src/renderer/src/components/mobile/MobilePage.tsx index 434f0298626..416efd2ac30 100644 --- a/src/renderer/src/components/mobile/MobilePage.tsx +++ b/src/renderer/src/components/mobile/MobilePage.tsx @@ -62,6 +62,13 @@ export default function MobilePage(): React.JSX.Element { const [refreshingNetworkInterfaces, setRefreshingNetworkInterfaces] = useState(false) const hasGeneratedRef = useRef(false) const pairingRequestIdRef = useRef(0) + // Why: each flow entry starts its own address lookup. Gating the Step 2 mint on + // "has this visit's lookup settled" is false until it answers, where "is a lookup + // running" cannot tell overlapping lookups apart and clears on the first to land. + const [pairingFlowVisit, setPairingFlowVisit] = useState(0) + const [addressedFlowVisit, setAddressedFlowVisit] = useState(null) + const pairingAddressSettled = addressedFlowVisit === pairingFlowVisit + const networkInterfacesRequestIdRef = useRef(0) const mountedRef = useMountedRef() const closeMobilePage = useAppStore((s) => s.closeMobilePage) const showMobileButton = useAppStore((s) => s.settings?.showMobileButton !== false) @@ -188,23 +195,33 @@ export default function MobilePage(): React.JSX.Element { }) const loadNetworkInterfaces = useCallback(async () => { + const requestId = ++networkInterfacesRequestIdRef.current + const visit = pairingFlowVisit if (mountedRef.current) { setRefreshingNetworkInterfaces(true) } try { const result = await window.api.mobile.listNetworkInterfaces() - if (mountedRef.current) { + // Why: a superseded lookup must not move the selection a newer one already + // resolved — that address change remints over the offer just advertised. + if (mountedRef.current && requestId === networkInterfacesRequestIdRef.current) { setNetworkInterfaces(result.interfaces) selectAddressAfterRefresh(result.interfaces) } } catch { // Network list is non-critical; the QR will still mint with default routing. } finally { - if (mountedRef.current) { + // Why: only the newest lookup may report a completion — a superseded one + // marking its visit addressed releases the mint against an address its own + // replacement is about to change. Plain assignment is safe because entering + // a flow bumps the visit and starts its own lookup, so the newest request + // always carries the highest visit. + if (mountedRef.current && requestId === networkInterfacesRequestIdRef.current) { + setAddressedFlowVisit(visit) setRefreshingNetworkInterfaces(false) } } - }, [mountedRef, selectAddressAfterRefresh]) + }, [mountedRef, pairingFlowVisit, selectAddressAfterRefresh]) useEffect(() => { if (stage !== 'flow') { @@ -263,30 +280,39 @@ export default function MobilePage(): React.JSX.Element { if (!canGenerate) { return } + // Why: entering Step 2 also starts this visit's address lookup, and minting + // before it settles advertises an address the lookup is about to replace — the + // replacement then rotates away the credential this mint just created, so one + // Continue runs two overlapping offers through main for one pending token. + if (!pairingAddressSettled) { + return + } void generatePairing(false) - }, [stage, stepIdx, canGenerate, generatePairing]) + }, [stage, stepIdx, canGenerate, generatePairing, pairingAddressSettled]) // Why: entering the flow must mint a fresh pairing token — clear stale QR // state so we never flash an expired code from a previous session. - const enterFlow = (): void => { + const beginPairingVisit = (): void => { + pairingRequestIdRef.current += 1 + setPairLoading(false) + setPairingFlowVisit((visit) => visit + 1) hasGeneratedRef.current = false setPairQrDataUrl(null) setPairQrSize(null) setPairingUrl(null) setPairingQrError(false) setRelayMintFailure(null) + } + + const enterFlow = (): void => { + beginPairingVisit() showFirstPairingFlow() } // Why: from the paired summary, "Pair another device" jumps straight to // Step 2 since the app is presumably already installed on the user's phone. const pairAnotherDevice = (): void => { - hasGeneratedRef.current = false - setPairQrDataUrl(null) - setPairQrSize(null) - setPairingUrl(null) - setPairingQrError(false) - setRelayMintFailure(null) + beginPairingVisit() showPairAnotherDeviceFlow() } @@ -311,6 +337,14 @@ export default function MobilePage(): React.JSX.Element { useMobilePageEscape(closeMobilePage) + // Why: while the deferred first mint waits on the address, Step 2 would + // otherwise read "Generate a pairing code to continue" — a prompt for work it + // is already about to do on the user's behalf. Kept separate from pairLoading: + // that one feeds the invalidation hook's shouldRegenerate, so folding this into + // it would let a mode switch mint before the address settles. + const awaitingPairingAddress = + stage === 'flow' && stepIdx === 1 && canGenerate && !pairingAddressSettled + return ( Date: Thu, 17 Sep 2026 21:21:01 -0700 Subject: [PATCH 081/168] fix(terminal): keep a split's real direction when the leaf set moves (#21294) resolveTerminalLayoutRoot discarded any known tree that did not cover the published leaf set exactly and rebuilt the tab as a flat chain with a guessed 'horizontal' direction, restacking side-by-side panes. The guess is then published, mirrored to every paired client, and written back over the real tree, so the direction is gone from disk. Prune a known tree to the leaves that survive and graft only the leaves no tree places, which is now the sole place a direction is invented and is still reported through onSynthesize. --- config/scripts/pr-e2e-source-routing.mjs | 11 + .../remote-terminal-layout-resolution.test.ts | 88 ++++++++ .../remote-terminal-layout-resolution.ts | 102 ++++++--- .../sync-runtime-graph/graph-publication.ts | 2 +- .../mobile-session-terminal-tabs.ts | 2 +- .../terminal-surfaces.ts | 4 +- ...shed-split-orientation-legacy-leaf.spec.ts | 202 ++++++++++++++++++ 7 files changed, 378 insertions(+), 33 deletions(-) create mode 100644 tests/e2e/desktop-published-split-orientation-legacy-leaf.spec.ts diff --git a/config/scripts/pr-e2e-source-routing.mjs b/config/scripts/pr-e2e-source-routing.mjs index 3b8f2e90afb..b9c7c387716 100644 --- a/config/scripts/pr-e2e-source-routing.mjs +++ b/config/scripts/pr-e2e-source-routing.mjs @@ -178,6 +178,17 @@ export const PR_E2E_SOURCE_ROUTES = [ file ) }, + { + // Why: layout resolution is the only place a split direction can be invented, and the + // loss is one-way — the guess is published and written back over the real tree. + id: 'terminal-session.split-orientation-resolution', + specs: ['tests/e2e/desktop-published-split-orientation-legacy-leaf.spec.ts'], + matches: (file) => + isProductSource(file) && + /^src\/renderer\/src\/runtime\/(?:remote-terminal-layout-resolution\.ts|sync-runtime-graph\/(?:graph-publication|mobile-session-terminal-tabs|mobile-session-surfaces)\.ts|web-session-tabs-sync\/terminal-surfaces\.ts)$/.test( + file + ) + }, { id: 'terminal-session.remote-pane-layout-retry', specs: ['tests/e2e/paired-remote-pane-layout-retry.spec.ts'], diff --git a/src/renderer/src/runtime/remote-terminal-layout-resolution.test.ts b/src/renderer/src/runtime/remote-terminal-layout-resolution.test.ts index a47d09350ad..2c1f100cd52 100644 --- a/src/renderer/src/runtime/remote-terminal-layout-resolution.test.ts +++ b/src/renderer/src/runtime/remote-terminal-layout-resolution.test.ts @@ -69,6 +69,94 @@ describe('resolveTerminalLayoutRoot', () => { expect(resolveTerminalLayoutRoot({ leafIds: [] })).toBeNull() }) + it('prunes a superset tree to the live leaves instead of re-guessing its directions', () => { + // A stale/extra leaf in the known tree used to fail the exact-cover check and + // collapse the whole tab to a guessed chain. + const onSynthesize = vi.fn() + const root = resolveTerminalLayoutRoot({ + authoritativeRoot: { + type: 'split', + direction: 'horizontal', + first: verticalSplit, + second: { type: 'leaf', leafId: 'stale' } + }, + leafIds: ['a', 'b'], + onSynthesize + }) + expect(root).toEqual(verticalSplit) + expect(onSynthesize).not.toHaveBeenCalled() + }) + + it('collapses a split that loses one child and keeps the outer direction', () => { + const root = resolveTerminalLayoutRoot({ + authoritativeRoot: { + type: 'split', + direction: 'vertical', + first: { + type: 'split', + direction: 'horizontal', + first: { type: 'leaf', leafId: 'a' }, + second: { type: 'leaf', leafId: 'b' } + }, + second: { type: 'leaf', leafId: 'c' } + }, + leafIds: ['a', 'c'] + }) + expect(root).toEqual({ + type: 'split', + direction: 'vertical', + first: { type: 'leaf', leafId: 'a' }, + second: { type: 'leaf', leafId: 'c' } + }) + }) + + it('grafts a genuinely new leaf without disturbing the directions already known', () => { + // Only the new leaf's placement is a guess; the vertical split must survive it. + const onSynthesize = vi.fn() + const root = resolveTerminalLayoutRoot({ + authoritativeRoot: verticalSplit, + leafIds: ['a', 'b', 'c'], + onSynthesize + }) + expect(root).toEqual({ + type: 'split', + direction: 'horizontal', + first: verticalSplit, + second: { type: 'leaf', leafId: 'c' } + }) + expect(onSynthesize).toHaveBeenCalledWith(1) + }) + + it('keeps the prior client tree when the host tree places fewer of the leaves', () => { + const root = resolveTerminalLayoutRoot({ + authoritativeRoot: { type: 'leaf', leafId: 'a' }, + existingRoot: verticalSplit, + leafIds: ['a', 'b', 'c'] + }) + expect(root).toEqual({ + type: 'split', + direction: 'horizontal', + first: verticalSplit, + second: { type: 'leaf', leafId: 'c' } + }) + }) + + it('still degenerates when no known tree places any of the leaves', () => { + const onSynthesize = vi.fn() + const root = resolveTerminalLayoutRoot({ + authoritativeRoot: verticalSplit, + leafIds: ['x', 'y'], + onSynthesize + }) + expect(root).toEqual({ + type: 'split', + direction: 'horizontal', + first: { type: 'leaf', leafId: 'x' }, + second: { type: 'leaf', leafId: 'y' } + }) + expect(onSynthesize).toHaveBeenCalledWith(2) + }) + it('prefers authoritative over an also-covering existing tree', () => { const horizontalSplit: TerminalPaneLayoutNode = { type: 'split', diff --git a/src/renderer/src/runtime/remote-terminal-layout-resolution.ts b/src/renderer/src/runtime/remote-terminal-layout-resolution.ts index 1691132227b..918697feeeb 100644 --- a/src/renderer/src/runtime/remote-terminal-layout-resolution.ts +++ b/src/renderer/src/runtime/remote-terminal-layout-resolution.ts @@ -7,11 +7,13 @@ import type { TerminalPaneLayoutNode } from '../../../shared/terminal-tab-types' * of independently re-deriving it (which is how "Split Right" used to render as * a down split — divergent fallbacks each guessed a direction). * - * Invariant: NEVER invent a split direction. A split's direction is meaningful - * user/host state, so a guessed direction is wrong by construction. When no - * authoritative tree covers the leaves, we keep whatever covering tree we do - * have, and only as a true last resort synthesize a degenerate chain — logged - * so the gap is visible rather than silently masquerading as a real layout. + * Invariant: NEVER invent a split direction for a leaf some known tree already + * places. A split's direction is meaningful user/host state, and the resolved + * tree is persisted and pushed back to the host, so a guess that wins here + * destroys the real direction on disk — a one-way door. A known tree that does + * not match the leaf set exactly is still knowledge: it is pruned to the leaves + * that survive and grafted with only the genuinely new ones, which is the sole + * place a direction is invented (and always reported). */ function collectLayoutLeafIds( @@ -47,40 +49,63 @@ export function layoutCoversLeaves( } /** - * Last-resort tree when no authoritative or prior layout covers the leaves. - * A single leaf needs no direction; >1 leaf cannot be rendered as a split - * without inventing one, so this path is degenerate and should not fire for a - * real split — callers pass `onSynthesize` to surface when it does. + * Drop every leaf outside `keep`; a split that loses one child collapses to the + * other. Surviving splits keep the direction the user/host actually chose. */ -function synthesizeDegenerateLayout( - leafIds: readonly string[], - onSynthesize?: (leafCount: number) => void +export function pruneLayoutToLeaves( + node: TerminalPaneLayoutNode | null | undefined, + keep: ReadonlySet ): TerminalPaneLayoutNode | null { - if (leafIds.length === 0) { + if (!node) { return null } - if (leafIds.length === 1) { - return { type: 'leaf', leafId: leafIds[0]! } + if (node.type === 'leaf') { + return keep.has(node.leafId) ? node : null } - onSynthesize?.(leafIds.length) - // No known direction: stack left-to-right as a flat chain. This is a visible - // fallback, not a guess we want to win — see invariant above. - return leafIds.slice(1).reduce( - (root, leafId) => ({ - type: 'split', - direction: 'horizontal', - first: root, - second: { type: 'leaf', leafId } - }), - { type: 'leaf', leafId: leafIds[0]! } + const first = pruneLayoutToLeaves(node.first, keep) + const second = pruneLayoutToLeaves(node.second, keep) + if (first && second) { + return first === node.first && second === node.second ? node : { ...node, first, second } + } + return first ?? second +} + +/** + * Attach leaves no known tree describes. This is the only direction we invent, + * so it is always reported; the retained subtree keeps its real directions. + */ +function graftUnplacedLeaves( + root: TerminalPaneLayoutNode | null, + unplacedLeafIds: readonly string[] +): TerminalPaneLayoutNode | null { + return unplacedLeafIds.reduce( + (tree, leafId) => + tree === null + ? { type: 'leaf', leafId } + : { type: 'split', direction: 'horizontal', first: tree, second: { type: 'leaf', leafId } }, + root ) } +/** How many of `leafIds` this tree already places — its value as a donor. */ +function countPlacedLeaves( + root: TerminalPaneLayoutNode | null | undefined, + leafIds: readonly string[] +): number { + if (!root) { + return 0 + } + const treeLeafIds = collectLayoutLeafIds(root) + return leafIds.filter((leafId) => treeLeafIds.has(leafId)).length +} + /** * Resolve the layout tree for `leafIds`, preferring authoritative/known trees - * (which carry the real direction) over any synthesized fallback. + * (which carry the real direction) over any invented structure. * - * Precedence: host-authoritative layout → prior client layout → degenerate. + * Precedence: a tree covering the leaves exactly (host-authoritative, then + * prior client) → the tree placing the most leaves, pruned to them and grafted + * with the rest → a degenerate chain when nothing is known. */ export function resolveTerminalLayoutRoot(args: { authoritativeRoot?: TerminalPaneLayoutNode | null @@ -94,5 +119,24 @@ export function resolveTerminalLayoutRoot(args: { if (layoutCoversLeaves(args.existingRoot, args.leafIds)) { return args.existingRoot ?? null } - return synthesizeDegenerateLayout(args.leafIds, args.onSynthesize) + if (args.leafIds.length === 0) { + return null + } + const authoritativePlaced = countPlacedLeaves(args.authoritativeRoot, args.leafIds) + const existingPlaced = countPlacedLeaves(args.existingRoot, args.leafIds) + // Ties go to the host tree; it is the authority for direction. + const donor = + authoritativePlaced === 0 && existingPlaced === 0 + ? null + : authoritativePlaced >= existingPlaced + ? args.authoritativeRoot + : args.existingRoot + const retained = pruneLayoutToLeaves(donor, new Set(args.leafIds)) + const placed = collectLayoutLeafIds(retained) + const unplaced = args.leafIds.filter((leafId) => !placed.has(leafId)) + // One leaf and nothing retained is a bare leaf, which carries no direction. + if (unplaced.length > (retained === null ? 1 : 0)) { + args.onSynthesize?.(unplaced.length) + } + return graftUnplacedLeaves(retained, unplaced) } diff --git a/src/renderer/src/runtime/sync-runtime-graph/graph-publication.ts b/src/renderer/src/runtime/sync-runtime-graph/graph-publication.ts index f6457fcc454..f74c97f6e41 100644 --- a/src/renderer/src/runtime/sync-runtime-graph/graph-publication.ts +++ b/src/renderer/src/runtime/sync-runtime-graph/graph-publication.ts @@ -165,7 +165,7 @@ export async function syncRuntimeGraph(): Promise { leafIds: liveLeaves.map(([leafId]) => leafId), onSynthesize: (leafCount) => console.warn( - `[sync-runtime-graph] synthesized layout for ${leafCount} unmounted leaves with no saved tree` + `[sync-runtime-graph] synthesized a split direction for ${leafCount} unmounted leaves no saved tree placed` ) }) }) diff --git a/src/renderer/src/runtime/sync-runtime-graph/mobile-session-terminal-tabs.ts b/src/renderer/src/runtime/sync-runtime-graph/mobile-session-terminal-tabs.ts index b1c7fe4b54f..ac88ad17eb6 100644 --- a/src/renderer/src/runtime/sync-runtime-graph/mobile-session-terminal-tabs.ts +++ b/src/renderer/src/runtime/sync-runtime-graph/mobile-session-terminal-tabs.ts @@ -49,7 +49,7 @@ export function buildMobileTerminalSurfaceTabs( leafIds, onSynthesize: (leafCount) => console.warn( - `[sync-runtime-graph] synthesized parentLayout for ${leafCount} leaves with no live or saved tree` + `[sync-runtime-graph] synthesized a parentLayout split direction for ${leafCount} leaves no live or saved tree placed` ) }), activeLeafId, diff --git a/src/renderer/src/runtime/web-session-tabs-sync/terminal-surfaces.ts b/src/renderer/src/runtime/web-session-tabs-sync/terminal-surfaces.ts index 78169094429..233993ec171 100644 --- a/src/renderer/src/runtime/web-session-tabs-sync/terminal-surfaces.ts +++ b/src/renderer/src/runtime/web-session-tabs-sync/terminal-surfaces.ts @@ -201,14 +201,14 @@ export function chooseRemoteTerminalLayout( ? parentLayout.expandedLeafId : null return { - // Why: host parentLayout is authoritative for split direction; else keep the prior client tree, then degenerate — never re-guess a direction. + // Why: host parentLayout is authoritative for split direction; else keep the prior client tree — a leaf-set mismatch prunes/grafts it, never re-guesses the directions it already carries. root: resolveTerminalLayoutRoot({ authoritativeRoot: parentLayout?.root, existingRoot: existingLayout?.root, leafIds, onSynthesize: (leafCount) => console.warn( - `[web-session-tabs-sync] synthesized layout for ${leafCount} leaves; no authoritative or prior tree covered them` + `[web-session-tabs-sync] synthesized a split direction for ${leafCount} leaves no authoritative or prior tree placed` ) }), activeLeafId, diff --git a/tests/e2e/desktop-published-split-orientation-legacy-leaf.spec.ts b/tests/e2e/desktop-published-split-orientation-legacy-leaf.spec.ts new file mode 100644 index 00000000000..0a0ff09f07a --- /dev/null +++ b/tests/e2e/desktop-published-split-orientation-legacy-leaf.spec.ts @@ -0,0 +1,202 @@ +import type { Page } from '@stablyai/playwright-test' +import type { + TerminalLayoutSnapshot, + TerminalPaneLayoutNode +} from '../../src/shared/terminal-tab-types' +import { expect, test } from './helpers/orca-app' +import { + callPairedRuntime, + waitForPairedClientWorktree +} from './helpers/paired-client-host-session' +import { + createRuntimeDesktopPairingOffer, + launchPairedElectronClient, + type PairedElectronClient +} from './helpers/paired-electron-client' +import { parkHiddenTabBehindDecoy } from './helpers/terminal-hidden-parking' +import { + readPaneIdentitySnapshot, + resolveActiveTabId, + splitActiveTerminalPane, + waitForActiveTerminalManager, + waitForPaneCount +} from './helpers/terminal' + +/** + * A desktop app republishes an unmounted terminal tab's layout from its own saved tree, and + * derives the leaf set from that tree's stable-id leaves. A leaf id that predates the stable-id + * scheme drops out of the leaf set but stays in the tree, so the tree stopped covering the leaf + * set exactly — and the publisher used to answer that by discarding the tree and chaining every + * leaf with a guessed "horizontal", restacking a side-by-side split for every paired client and + * for the record they all write back. The real direction has to survive the mismatch. + */ + +/** Legacy pane id shape: not a stable pane UUID, so it never reaches the published leaf set. */ +const LEGACY_LEAF_ID = 'pane:9' + +/** + * Shrinks both the cold-park delay and the hot-retain window. Set at module scope because the + * `orcaPage` fixture launches the app before any test body runs. + */ +const PARK_DELAY_MS = 2_000 +process.env.ORCA_E2E_TERMINAL_PARKING_DELAY_MS ??= String(PARK_DELAY_MS) + +function collectLeafIds(node: TerminalPaneLayoutNode | null | undefined): string[] { + if (!node) { + return [] + } + return node.type === 'leaf' + ? [node.leafId] + : [...collectLeafIds(node.first), ...collectLeafIds(node.second)] +} + +/** Direction of the split that separates the two leaves, or null if one side holds both. */ +function splitDirectionSeparating( + node: TerminalPaneLayoutNode | null | undefined, + leafA: string, + leafB: string +): 'horizontal' | 'vertical' | null { + if (!node || node.type === 'leaf') { + return null + } + const firstLeaves = new Set(collectLeafIds(node.first)) + const secondLeaves = new Set(collectLeafIds(node.second)) + if ( + (firstLeaves.has(leafA) && secondLeaves.has(leafB)) || + (firstLeaves.has(leafB) && secondLeaves.has(leafA)) + ) { + return node.direction + } + return ( + splitDirectionSeparating(node.first, leafA, leafB) ?? + splitDirectionSeparating(node.second, leafA, leafB) + ) +} + +function readSavedLayout(page: Page, tabId: string): Promise { + return page.evaluate((id) => window.__store?.getState().terminalLayoutsByTabId[id] ?? null, tabId) +} + +type PublishedTerminalSurface = { + type: string + parentTabId?: string + leafId?: string + parentLayout?: TerminalLayoutSnapshot +} + +async function readPublishedTerminalSurfaces( + client: PairedElectronClient, + worktreeId: string, + hostTabId: string +): Promise { + const snapshot = await callPairedRuntime<{ tabs: PublishedTerminalSurface[] }>( + client.page, + client.environmentId, + 'session.tabs.list', + { worktree: `id:${worktreeId}` } + ) + return snapshot.tabs.filter((tab) => tab.type === 'terminal' && tab.parentTabId === hostTabId) +} + +test('publishes an unmounted split with its real orientation when a legacy leaf lingers in the saved tree', async ({ + orcaPage +}, testInfo) => { + test.setTimeout(360_000) + const worktreeId = await orcaPage.evaluate(() => window.__store?.getState().activeWorktreeId) + if (!worktreeId) { + throw new Error('Headed host has no active seeded workspace') + } + let client: PairedElectronClient | null = null + + try { + await waitForActiveTerminalManager(orcaPage, 60_000) + const hostTabId = await resolveActiveTabId(orcaPage) + if (!hostTabId) { + throw new Error('Headed host has no active terminal tab') + } + + // Split right: two panes side by side, the orientation the report is about. + await splitActiveTerminalPane(orcaPage, 'vertical') + await waitForPaneCount(orcaPage, 2, 60_000) + const panes = await readPaneIdentitySnapshot(orcaPage) + const leafIds = (panes?.panes ?? []).map((pane) => pane.leafId) + const [firstLeafId, secondLeafId] = leafIds + if (leafIds.length !== 2 || !firstLeafId || !secondLeafId) { + throw new Error(`Expected two split leaves, saw ${JSON.stringify(leafIds)}`) + } + + await expect + .poll( + async () => + splitDirectionSeparating( + (await readSavedLayout(orcaPage, hostTabId))?.root, + firstLeafId, + secondLeafId + ), + { timeout: 60_000, message: 'host never saved the side-by-side split' } + ) + .toBe('vertical') + + // Park the tab: a parked tab is republished from the saved tree, not the live DOM. + await parkHiddenTabBehindDecoy(orcaPage, worktreeId, hostTabId, { + parkDelayMs: PARK_DELAY_MS + }) + + // The drift under test: the saved tree keeps a leaf the stable-id leaf set cannot carry. + await orcaPage.evaluate( + ({ tabId, firstLeafId, secondLeafId, legacyLeafId }) => { + const state = window.__store?.getState() + const saved = state?.terminalLayoutsByTabId[tabId] + if (!state || !saved) { + throw new Error('No saved layout to seed the legacy leaf into') + } + state.setTabLayout(tabId, { + ...saved, + root: { + type: 'split', + direction: 'horizontal', + first: { + type: 'split', + direction: 'vertical', + first: { type: 'leaf', leafId: firstLeafId }, + second: { type: 'leaf', leafId: secondLeafId } + }, + second: { type: 'leaf', leafId: legacyLeafId } + } + }) + }, + { tabId: hostTabId, firstLeafId, secondLeafId, legacyLeafId: LEGACY_LEAF_ID } + ) + // Control: with no lingering leaf the saved tree covers the leaf set and the publisher + // never reaches the fallback at all, so the assertions below pass for free. + expect(collectLeafIds((await readSavedLayout(orcaPage, hostTabId))?.root)).toContain( + LEGACY_LEAF_ID + ) + + const offer = await createRuntimeDesktopPairingOffer(orcaPage) + client = await launchPairedElectronClient(offer, testInfo, 'legacy-leaf-orientation-observer') + await waitForPairedClientWorktree(client.page, worktreeId) + + await expect + .poll( + async () => + (await readPublishedTerminalSurfaces(client!, worktreeId, hostTabId)) + .map((surface) => surface.leafId) + .filter((leafId): leafId is string => typeof leafId === 'string') + .sort(), + { + timeout: 90_000, + message: 'host never published both split leaves to the paired client' + } + ) + .toEqual(expect.arrayContaining([firstLeafId, secondLeafId].sort())) + const published = await readPublishedTerminalSurfaces(client, worktreeId, hostTabId) + // Control: the leaf set really does exclude the leaf the saved tree still carries, so the + // publisher reached the mismatch path instead of using the tree verbatim. + expect(published.map((surface) => surface.leafId)).not.toContain(LEGACY_LEAF_ID) + const publishedRoot = published.find((surface) => surface.parentLayout)?.parentLayout?.root + expect(splitDirectionSeparating(publishedRoot, firstLeafId, secondLeafId)).toBe('vertical') + } finally { + await client?.dispose() + } +}) From 5c8540948d41e2c81f0b630811d4610a4a1b8fb8 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 21:21:14 -0700 Subject: [PATCH 082/168] test(e2e): name the paired-client quit that preserves the profile (#21300) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quit-without-deleting is closeElectronAppForE2E + cleanupE2EDaemons — dispose's first two steps without removeProfile. The composition is correct today but undiscoverable, and getting it wrong is silent and expensive in both directions. dispose() + reuseUserDataDir yields a FIRST RUN on an empty profile, so every persistence assertion after it reads empty and is indistinguishable from data loss. That produced a phantom data-loss report, live in two write-ups before a diagnostic listing zero session FILES (rather than zero buffers) contradicted it. Reaching for a bare app.close() to skip the deletion hangs instead: it lacks the timeout and force-kill fallback that closeElectronAppForE2E wraps around it, and burned a ten minute test deadline producing no reading at all. Test infrastructure only; no production code. Unblocks restart-persistence coverage for the paired topology. --- tests/e2e/helpers/paired-electron-client.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/e2e/helpers/paired-electron-client.ts b/tests/e2e/helpers/paired-electron-client.ts index a5947bc1228..46597415668 100644 --- a/tests/e2e/helpers/paired-electron-client.ts +++ b/tests/e2e/helpers/paired-electron-client.ts @@ -34,7 +34,15 @@ export type PairedElectronClient = { page: Page environmentId: string captureDirectSshAttempts: () => Promise + /** Closes the app AND deletes the profile. For a restart, use `quitPreservingProfile`. */ dispose: () => Promise + /** Quit for a relaunch on the same profile: everything `dispose` does except `removeProfile`. + * Why named rather than left to callers: composing it wrong is silent and expensive. Calling + * `dispose` and relaunching with `reuseUserDataDir` yields a FIRST RUN on an empty profile, so + * every persistence assertion after it reads empty and looks exactly like data loss — that + * produced a phantom data-loss report once. Reaching for a bare `app.close()` instead hangs: + * it lacks the timeout and force-kill fallback that `closeElectronAppForE2E` wraps around it. */ + quitPreservingProfile: () => Promise getDirectSshAttemptTargetIds: () => Promise installDirectSshAttemptProbe: () => Promise replacePairingInPlace: (offer: RuntimeDesktopPairingOffer) => Promise @@ -240,6 +248,10 @@ export async function launchPairedElectronClient( await cleanupE2EDaemons(userDataDir) await removeProfile(userDataDir) }, + quitPreservingProfile: async () => { + await closeElectronAppForE2E(app) + await cleanupE2EDaemons(userDataDir) + }, getDirectSshAttemptTargetIds: async () => { return readDirectSshAttemptTargetIds(directSshProbePath).filter( (targetId) => targetId !== DIRECT_SSH_PROBE_CANARY_TARGET_ID From 78a17bb24de085992edeb38ce47f6b71227e97da Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 21:21:27 -0700 Subject: [PATCH 083/168] fix(relay): one malformed pre-auth handshake frame closes its connection, not the daemon (#19879) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(relay): one malformed pre-auth handshake frame closes its connection, not the daemon parseHandshakeMessage returned whatever JSON.parse produced, and the daemon interpolates the peer's version into a log line before any credential check. A version that is an object with a non-callable toString throws TypeError there, inside the frame-decoder callback. FrameDecoder.drainTurn wrapped its synchronous dispatch in try/finally with no catch, so the throw escaped feed(), escaped the socket data handler, and reached uncaughtException: the relay daemon exited and every PTY and agent session it held died with it. Two layers, because only the second closes the class: - parseHandshakeMessage now requires the string fields each arm carries (version; expected/got) and rejects a non-object payload. Both readers share the parser, so neither side can interpolate a non-string again. - FrameDecoder contains a frame owner that throws on the synchronous turn the same way it already contained one on a continuation turn: reset the residue and report one FrameDecoderContinuationError to onError. Every owner's onError already closes its own connection, so any future throw of this shape costs one connection instead of the process. The relay CLI channel gains an explicit onError so a malformed reply still ends that one-shot command instead of parking it. * fix(relay): keep the diagnostic the refusal path exists to produce Two error paths that destroy their own evidence. `parseHandshakeMessage`'s unknown-type refusal interpolated `String(t)` on a peer-supplied value: `{"type":{"toString":1}}` makes String() throw "Cannot convert object to primitive value", so the refusal arrives without naming what was refused. `describeRelayProtocolVersion` guards this exact hazard two files away; the sibling was missed. `runRelayOrcaCliChannel`'s new `onDecodeError` wrote to stderr and then exited synchronously. stderr is async on a pipe transport, so the one line recording why the command died could be dropped — the reason relay-handshake.ts already exits inside its write callback. * fix(relay): prove the optional handshake field too, not just the required ones The parser refuses a non-string `version`, `expected` and `got`, then returns the object with `endpointCredential` unproved — the most pre-auth field on the frame. It is safe today only by accident: its one reader compares it, and a non-string loses that comparison. Nothing holds that shape in place, and the next reader to put it in a log line reinstates the template-literal throw this function exists to stop. Present-but-not-a-string is now refused at the parser. Absent stays absent: a bridge presenting no credential is the common case, and refusing it would close every unauthenticated-endpoint connection. Wire-visible delta, deliberate: a peer sending a non-string credential used to get `orca-relay-handshake-credential-mismatch` and exit 43; it now gets a bare close. No first-party client can reach it — `runConnectHandshake` types the parameter `string` and omits it when falsy — and a bare close is the right answer to a frame that was malformed before any credential was checked. * fix(relay): carry the SAFETY: rationale main's casting gate now requires Main gained a `typescript/consistent-type-assertions` scan while this branch sat 432 commits behind, so every `as` the branch touches lands as a new finding. The parser is the one place the handshake shape is proved, so each cast names the check that earns it, and the hostile-frame cast in the round-trip test names the fact that it is a deliberate lie the type system cannot describe. * test(relay): annotate the hostile handshake frame instead of suppressing a cast JSON.parse answers `any`, so a typed const expresses the same deliberate lie the assertion did and the casting gate has nothing to flag. One fewer suppression. --- .../ssh/relay-protocol-backpressure.test.ts | 34 ++++++++ src/relay/protocol-backpressure.test.ts | 35 +++++++++ src/relay/protocol-handshake.test.ts | 78 +++++++++++++++++++ src/relay/protocol.ts | 64 ++++++++++++--- src/relay/relay-handshake-roundtrip.test.ts | 66 ++++++++++++++++ src/relay/relay-orca-cli-channel.ts | 13 +++- src/shared/relay-frame-decoder.ts | 22 ++++-- 7 files changed, 295 insertions(+), 17 deletions(-) diff --git a/src/main/ssh/relay-protocol-backpressure.test.ts b/src/main/ssh/relay-protocol-backpressure.test.ts index a1ee62655e8..45a869af16e 100644 --- a/src/main/ssh/relay-protocol-backpressure.test.ts +++ b/src/main/ssh/relay-protocol-backpressure.test.ts @@ -200,6 +200,40 @@ describe('FrameDecoder bounded turns', () => { expect(seen).toEqual([1, 4]) }) + // feed() runs straight from a transport data handler. A frame owner that threw on the first + // turn used to escape feed() and reach uncaughtException. The continuation path was already + // contained; the synchronous path must match it. + it('contains a frame owner that throws on the synchronous turn and reports one typed error', () => { + const seen: number[] = [] + const onError = vi.fn() + const pause = vi.fn() + const resume = vi.fn() + const decoder = new FrameDecoder( + (decoded) => { + if (decoded.id === 2) { + throw new Error('frame owner failed') + } + seen.push(decoded.id) + }, + onError, + { pause, resume } + ) + + expect(() => decoder.feed(Buffer.concat([frame(1), frame(2), frame(3)]))).not.toThrow() + + expect(seen).toEqual([1]) + expect(onError).toHaveBeenCalledExactlyOnceWith(expect.any(FrameDecoderContinuationError)) + expect(onError.mock.calls[0]?.[0]).toMatchObject({ + cause: expect.objectContaining({ message: 'frame owner failed' }) + }) + expect(decoder.drain()).toHaveLength(0) + expect(pause).not.toHaveBeenCalled() + expect(resume).not.toHaveBeenCalled() + + decoder.feed(frame(4)) + expect(seen).toEqual([1, 4]) + }) + it('keeps reads active for partial frames and incrementally discards oversized payloads', () => { const errors: Error[] = [] const seen: DecodedFrame[] = [] diff --git a/src/relay/protocol-backpressure.test.ts b/src/relay/protocol-backpressure.test.ts index fa4724ff960..f2693fe941f 100644 --- a/src/relay/protocol-backpressure.test.ts +++ b/src/relay/protocol-backpressure.test.ts @@ -200,6 +200,41 @@ describe('relay FrameDecoder bounded turns', () => { expect(seen).toEqual([1, 4]) }) + // feed() runs straight from a socket 'data' handler. A frame owner that threw on the first + // turn used to escape feed() and reach uncaughtException, which in the relay daemon means every + // PTY and agent session it holds dies with it. The continuation path was already contained. + it('contains a frame owner that throws on the synchronous turn and reports one typed error', () => { + const seen: number[] = [] + const onError = vi.fn() + const pause = vi.fn() + const resume = vi.fn() + const decoder = new FrameDecoder( + (decoded) => { + if (decoded.id === 2) { + throw new Error('frame owner failed') + } + seen.push(decoded.id) + }, + onError, + { pause, resume } + ) + + expect(() => decoder.feed(Buffer.concat([frame(1), frame(2), frame(3)]))).not.toThrow() + + expect(seen).toEqual([1]) + expect(onError).toHaveBeenCalledExactlyOnceWith(expect.any(FrameDecoderContinuationError)) + expect(onError.mock.calls[0]?.[0]).toMatchObject({ + cause: expect.objectContaining({ message: 'frame owner failed' }) + }) + // Residue after the bad frame is dropped rather than replayed, and no pause epoch is leaked. + expect(decoder.drain()).toHaveLength(0) + expect(pause).not.toHaveBeenCalled() + expect(resume).not.toHaveBeenCalled() + + decoder.feed(frame(4)) + expect(seen).toEqual([1, 4]) + }) + it('keeps reads active for partial frames and incrementally discards oversized payloads', () => { const errors: Error[] = [] const seen: DecodedFrame[] = [] diff --git a/src/relay/protocol-handshake.test.ts b/src/relay/protocol-handshake.test.ts index 822fea3e20b..4b4695877b7 100644 --- a/src/relay/protocol-handshake.test.ts +++ b/src/relay/protocol-handshake.test.ts @@ -61,6 +61,84 @@ describe('handshake framing', () => { expect(() => parseHandshakeMessage(bogus)).toThrow(/Unknown handshake type/) }) + // `type` is peer-supplied, so it can be an object whose String() conversion throws — which + // replaced the one diagnostic this refusal exists to produce with a primitive-conversion error. + it('still names the refusal when the peer type cannot be stringified', () => { + const hostile = Buffer.from(JSON.stringify({ type: { toString: 1 } })) + expect(() => parseHandshakeMessage(hostile)).toThrow(/Unknown handshake type: object/) + }) + + // The daemon logs the peer's version before any credential check, and `JSON.parse` can hand + // back a value a template literal throws on. The parser is the one place every reader shares. + it('rejects a version that is not a string on both arms that carry one', () => { + for (const type of ['orca-relay-handshake', 'orca-relay-handshake-ok']) { + for (const version of [{ toString: 1 }, 7, null, undefined, ['0.1.0']]) { + const payload = Buffer.from(JSON.stringify({ type, version })) + expect( + () => parseHandshakeMessage(payload), + `${type} version=${JSON.stringify(version)}` + ).toThrow(/Handshake field version is not a string/) + } + } + }) + + it('rejects a mismatch reply whose expected or got is not a string', () => { + const type = 'orca-relay-handshake-mismatch' + expect(() => + parseHandshakeMessage(Buffer.from(JSON.stringify({ type, expected: {}, got: 'b' }))) + ).toThrow(/Handshake field expected is not a string/) + expect(() => + parseHandshakeMessage(Buffer.from(JSON.stringify({ type, expected: 'a', got: 1 }))) + ).toThrow(/Handshake field got is not a string/) + }) + + it('rejects payloads that are not objects', () => { + for (const payload of ['null', '"orca-relay-handshake"', '42']) { + expect(() => parseHandshakeMessage(Buffer.from(payload)), payload).toThrow( + /Handshake payload is not an object/ + ) + } + }) + + // endpointCredential is the one optional field, and it is the most pre-auth thing on the frame. + // Its only reader compares it, so a non-string refuses today by inequality rather than by type — + // which is luck, not a guarantee. Prove it at the parser, where every reader shares it. + it('rejects a present endpointCredential that is not a string', () => { + for (const endpointCredential of [{ toString: 1 }, 7, null, ['secret'], true]) { + const payload = Buffer.from( + JSON.stringify({ type: 'orca-relay-handshake', version: '0.1.0', endpointCredential }) + ) + expect( + () => parseHandshakeMessage(payload), + `endpointCredential=${JSON.stringify(endpointCredential)}` + ).toThrow(/Handshake field endpointCredential is not a string/) + } + }) + + // Absent must stay absent: a bridge that legitimately presents no credential is the common case, + // and refusing it here would close every unauthenticated-endpoint connection in the fleet. + it('still accepts a handshake with no endpointCredential, and one with a string', () => { + const bare = Buffer.from(JSON.stringify({ type: 'orca-relay-handshake', version: '0.1.0' })) + expect(parseHandshakeMessage(bare)).toEqual({ type: 'orca-relay-handshake', version: '0.1.0' }) + const withCredential = Buffer.from( + JSON.stringify({ type: 'orca-relay-handshake', version: '0.1.0', endpointCredential: 'sec' }) + ) + expect(parseHandshakeMessage(withCredential)).toEqual({ + type: 'orca-relay-handshake', + version: '0.1.0', + endpointCredential: 'sec' + }) + }) + + it('still accepts a credential-mismatch reply, which carries no fields', () => { + const payload = Buffer.from( + JSON.stringify({ type: 'orca-relay-handshake-credential-mismatch' }) + ) + expect(parseHandshakeMessage(payload)).toEqual({ + type: 'orca-relay-handshake-credential-mismatch' + }) + }) + it('handshake frames use a distinct MessageType from Regular and KeepAlive', () => { expect(MessageType.Handshake).not.toBe(MessageType.Regular) expect(MessageType.Handshake).not.toBe(MessageType.KeepAlive) diff --git a/src/relay/protocol.ts b/src/relay/protocol.ts index 0f31b448f55..84656fed3cb 100644 --- a/src/relay/protocol.ts +++ b/src/relay/protocol.ts @@ -50,18 +50,62 @@ export function encodeHandshakeFrame(msg: HandshakeMessage): Buffer { return encodeFrame(MessageType.Handshake, 0, 0, payload) } +// Why the fields are checked and not just the type: this frame arrives before any credential, and +// both sides interpolate its version fields into log lines. `JSON.parse` can produce values a +// template literal throws on, so anything that reaches a reader must already be a string. +const HANDSHAKE_STRING_FIELDS: Readonly> = { + 'orca-relay-handshake': ['version'], + 'orca-relay-handshake-ok': ['version'], + 'orca-relay-handshake-mismatch': ['expected', 'got'], + 'orca-relay-handshake-credential-mismatch': [] +} + +// Optional fields are peer-supplied too, so the parser only proves the type of what it returns if +// it refuses a present-but-wrong one. `endpointCredential` survives today only because its single +// reader compares it and never interpolates it; the next reader to log it would restore the bug +// this function exists to stop. Absent stays absent — refusing that would break a bridge that +// legitimately presents no credential. +const HANDSHAKE_OPTIONAL_STRING_FIELDS: Readonly< + Record +> = { + 'orca-relay-handshake': ['endpointCredential'], + 'orca-relay-handshake-ok': [], + 'orca-relay-handshake-mismatch': [], + 'orca-relay-handshake-credential-mismatch': [] +} + export function parseHandshakeMessage(payload: Buffer): HandshakeMessage { - const msg = JSON.parse(payload.toString('utf-8')) as HandshakeMessage - const t = (msg as { type?: string }).type - if ( - t !== 'orca-relay-handshake' && - t !== 'orca-relay-handshake-ok' && - t !== 'orca-relay-handshake-mismatch' && - t !== 'orca-relay-handshake-credential-mismatch' - ) { - throw new Error(`Unknown handshake type: ${t}`) + const parsed: unknown = JSON.parse(payload.toString('utf-8')) + if (typeof parsed !== 'object' || parsed === null) { + throw new Error('Handshake payload is not an object') } - return msg + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the typeof/null guard directly above is exactly what makes this an index-able object; every read below still proves its own field. + const msg = parsed as Record + const t = msg.type + const required = + typeof t === 'string' && Object.hasOwn(HANDSHAKE_STRING_FIELDS, t) + ? // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: reached only when Object.hasOwn proved t is a key of this record, on the same line. + HANDSHAKE_STRING_FIELDS[t as HandshakeMessage['type']] + : null + if (required === null) { + // Why typeof and not String(t): a peer-supplied `{ "type": { "toString": 1 } }` makes String() + // itself throw "Cannot convert object to primitive value", replacing the one diagnostic this + // line exists to produce. + throw new Error(`Unknown handshake type: ${typeof t === 'string' ? t : typeof t}`) + } + for (const field of required) { + if (typeof msg[field] !== 'string') { + throw new Error(`Handshake field ${field} is not a string`) + } + } + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the required === null bail above already refused every t that is not one of the four keys. + for (const field of HANDSHAKE_OPTIONAL_STRING_FIELDS[t as HandshakeMessage['type']]) { + if (msg[field] !== undefined && typeof msg[field] !== 'string') { + throw new Error(`Handshake field ${field} is not a string`) + } + } + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: this is the one place the shape is proved: the type is one of the four literals and every field the union declares has been checked to be a string. + return msg as unknown as HandshakeMessage } export const KEEPALIVE_SEND_MS = 5_000 diff --git a/src/relay/relay-handshake-roundtrip.test.ts b/src/relay/relay-handshake-roundtrip.test.ts index 0713d62295e..bfc345e8366 100644 --- a/src/relay/relay-handshake-roundtrip.test.ts +++ b/src/relay/relay-handshake-roundtrip.test.ts @@ -14,6 +14,7 @@ import { encodeJsonRpcFrame, FrameDecoder, type DecodedFrame, + type HandshakeMessage, MessageType } from './protocol' import { relayTestSocketPath } from './relay-test-socket-path' @@ -246,4 +247,69 @@ describe('handshake round-trip over a real Socket pair', () => { bridgeSock.destroy() }) + + // The daemon reads one handshake frame before any credential check, so every field on it is + // untrusted input. `JSON.parse` hands back objects a template literal cannot stringify, and the + // frame callback runs inside the decoder: a throw there used to escape the socket's data + // handler and take the daemon — and every PTY and agent session it held — down with it. + it('closes a connection whose handshake version is not a string and keeps serving', async () => { + const { accepted } = await startDaemon('0.1.0+server-version') + + const hostile = connect(sockPath) + await new Promise((r) => hostile.once('connect', () => r())) + const hostileClosed = new Promise((r) => hostile.once('close', () => r())) + // The annotation is deliberately a lie: this is the frame a hostile peer sends, and + // HandshakeMessage cannot describe it. JSON.parse answers `any`, so it needs no assertion. + const hostileFrame: HandshakeMessage = JSON.parse( + '{"type":"orca-relay-handshake","version":{"toString":1}}' + ) + hostile.write(encodeHandshakeFrame(hostileFrame)) + await hostileClosed + + const good = connect(sockPath) + await new Promise((r) => good.once('connect', () => r())) + const acceptedCb = vi.fn<(leftover: Buffer) => void>() + runConnectHandshake(good, '0.1.0+server-version', { onAccepted: acceptedCb }) + await accepted + await vi.waitFor(() => expect(acceptedCb).toHaveBeenCalledTimes(1)) + + good.destroy() + }) + + // Same class, different instance: `onAccepted` runs inside the frame callback too, so a throw + // from the accept path must cost that one connection and nothing else. + it('closes only the connection whose accept path throws', async () => { + let connections = 0 + const acceptedSockets: Socket[] = [] + server = createServer((sock) => { + trackServerSocket(sock) + connections += 1 + const failThisOne = connections === 1 + setupDaemonHandshake(sock, { + launchVersion: '0.1.0+server-version', + onAccepted: (s) => { + if (failThisOne) { + throw new Error('accept path failed') + } + acceptedSockets.push(s) + } + }) + }) + await new Promise((r) => server.listen(sockPath, () => r())) + + const first = connect(sockPath) + await new Promise((r) => first.once('connect', () => r())) + const firstClosed = new Promise((r) => first.once('close', () => r())) + runConnectHandshake(first, '0.1.0+server-version', { onAccepted: vi.fn() }) + await firstClosed + + const second = connect(sockPath) + await new Promise((r) => second.once('connect', () => r())) + const acceptedCb = vi.fn<(leftover: Buffer) => void>() + runConnectHandshake(second, '0.1.0+server-version', { onAccepted: acceptedCb }) + await vi.waitFor(() => expect(acceptedCb).toHaveBeenCalledTimes(1)) + expect(acceptedSockets).toHaveLength(1) + + second.destroy() + }) }) diff --git a/src/relay/relay-orca-cli-channel.ts b/src/relay/relay-orca-cli-channel.ts index 2afbd363fe5..1d528c63280 100644 --- a/src/relay/relay-orca-cli-channel.ts +++ b/src/relay/relay-orca-cli-channel.ts @@ -151,6 +151,17 @@ export async function runRelayOrcaCliChannel( } } + // Why an explicit error path: the decoder contains a throwing frame owner instead of letting + // it escape, so a malformed relay reply must still end this one-shot command, not park it. + const onDecodeError = (error: Error): void => { + // Why exit inside the write callback: stderr is async on pipe transports, so exiting early + // drops the only evidence this failure ever produces — the same reason relay-handshake.ts + // writes its mismatch line this way. + process.stderr.write(`[orca-cli] Relay protocol error: ${error.message}\n`, () => { + sock.destroy() + process.exit(1) + }) + } const decoder = new FrameDecoder((frame: DecodedFrame) => { if (frame.id > highestReceivedSeq) { highestReceivedSeq = frame.id @@ -194,7 +205,7 @@ export async function runRelayOrcaCliChannel( } sendPostOutput(result.postOutput) }) - }) + }, onDecodeError) const connectTimeout = setTimeout(() => { process.stderr.write(`[orca-cli] Relay connection timed out after ${CONNECT_TIMEOUT_MS}ms\n`) diff --git a/src/shared/relay-frame-decoder.ts b/src/shared/relay-frame-decoder.ts index 22a1c348185..e2b012593e3 100644 --- a/src/shared/relay-frame-decoder.ts +++ b/src/shared/relay-frame-decoder.ts @@ -143,12 +143,22 @@ export class FrameDecoder { const framed = this.buffer.take(totalLength) frames += 1 bytes += totalLength - this.onFrame({ - type: framed[0], - id: framed.readUInt32BE(1), - ack: framed.readUInt32BE(5), - payload: framed.subarray(HEADER_LENGTH, totalLength) - }) + // Why contain here and not in the caller: feed() runs straight from a socket 'data' + // handler, so a frame owner that throws on the first turn would escape as an + // uncaughtException and take the whole process — and every connection it serves — down. + // The continuation path already contains this; the synchronous path must match it, so + // one bad frame costs one connection (the owner's onError closes it), never the process. + try { + this.onFrame({ + type: framed[0], + id: framed.readUInt32BE(1), + ack: framed.readUInt32BE(5), + payload: framed.subarray(HEADER_LENGTH, totalLength) + }) + } catch (error) { + // reset() bumps the generation, which ends this turn and drops the residue. + containFrameDecoderContinuation(() => this.reset(), this.onError, error) + } } } finally { this.draining = false From edbcf68e537824ee42a388f14b966580cdc13218 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 21:21:53 -0700 Subject: [PATCH 084/168] fix(ssh): record the superseded-relay pass the Windows arm abandons (#20045) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(ssh): record the superseded-relay pass the Windows arm abandons `sweepSupersededRelayEndpoints` returned `[]` for every Windows remote host and for every failed listing without writing a line. Both returns are indistinguishable from "this host had no orphans", which is the one thing this sweep exists not to be: its own header says it makes the orphan population "visible and deliberate rather than silent". The Windows population is real. `relayEndpointForHost` hashes the version directory into the pipe name, so an app update strands the incumbent exactly as it does on POSIX, and with `--grace-time 0` that relay keeps its PTYs and agents forever. Measured on a Windows 11 host (awin): the NPFS root lists 262 named pipes from an unprivileged shell, and the count of `orca-relay-*` names goes 0 -> 1 the moment a relay binds, so the endpoints are enumerable; the repo already enumerates them for GC via `relayLivenessProbeCommand`'s `.windows-active-pipe-*` marker scan. Reclaiming them is not this change. `probeRelayEndpointIncumbent` answers `unverifiable` for every Windows path, so nothing here could be classified, let alone reaped, and nothing about the kill path moves. What changes is that an abandoned pass now leaves a trace. * fix(ssh): keep the endpoints a half-run superseded sweep already classified The Windows arm and the failed-listing arm now both leave a line. The loop between them did not: socket 1 could be fully probed and classified, and an exec on socket 2 that threw took `logSupersededRelayFindings` with it — so a half-run pass and a host with nothing to sweep produced the same silence, and socket 1's verdict was lost. Only one failure class can leave that loop, and it is the one that matters: an exec whose SSH channel never confirmed close, which may still be running remotely and which `probeRelayEndpointIncumbent` rethrows by design. Every ordinary probe failure already degrades to `unverifiable` and the pass continues — a test now pins that too, so nobody "fixes" the loop into stopping on an absence of evidence. Findings are logged before the rethrow, which propagates unchanged. The added line says how far the pass got and claims nothing about the endpoints it never reached. * fix(ssh): word the Windows sweep skip so a first install does not read as orphaned The line fired on every Windows relay launch and asserted a population: "orphans from earlier builds are neither listed nor reclaimed" reads as a finding on a machine that has never had an earlier build. The skip is what is being recorded, not a census. --- .../ssh-relay-superseded-endpoints.test.ts | 63 ++++++++++++++++++- .../ssh/ssh-relay-superseded-endpoints.ts | 61 +++++++++++++----- 2 files changed, 109 insertions(+), 15 deletions(-) diff --git a/src/main/ssh/ssh-relay-superseded-endpoints.test.ts b/src/main/ssh/ssh-relay-superseded-endpoints.test.ts index 03f7e478905..dc091b67762 100644 --- a/src/main/ssh/ssh-relay-superseded-endpoints.test.ts +++ b/src/main/ssh/ssh-relay-superseded-endpoints.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' +import { beforeEach, describe, expect, it, vi, type MockInstance } from 'vitest' const execCommand = vi.fn() vi.mock('./ssh-relay-deploy-helpers', () => ({ @@ -46,6 +46,13 @@ function issuedCommands(): string[] { return execCommand.mock.calls.map((call) => String(call[1])) } +/** The `beforeEach` spy is reinstalled, not reset, so its calls survive the previous test. */ +function warnSpy(): MockInstance { + const spy = vi.spyOn(console, 'warn') + spy.mockClear() + return spy +} + beforeEach(() => { execCommand.mockReset() vi.spyOn(console, 'warn').mockImplementation(() => {}) @@ -177,8 +184,62 @@ describe('sweepSupersededRelayEndpoints', () => { await expect(sweepSupersededRelayEndpoints(CONN, HOST, SWEEP)).resolves.toEqual([]) }) + it('records the abandoned pass when the listing fails, so it reads apart from an empty host', async () => { + const warn = warnSpy() + execCommand.mockRejectedValueOnce(new Error('exec failed')) + await sweepSupersededRelayEndpoints(CONN, HOST, SWEEP) + expect(warn.mock.calls.flat().join('\n')).toContain('no pass ran: exec failed') + }) + + // Same defect as the two arms above, one level down. An ordinary probe failure degrades to + // `unverifiable` and the loop carries on, so the only way out of it mid-pass is the one case that + // matters most: an exec whose SSH channel never confirmed close, which may still be running + // remotely. That rethrows by design — and it used to throw past the log, losing socket 1's + // verdict and making a half-run pass read exactly like a host with nothing to sweep. + it('keeps the endpoints it already classified when a later probe cannot confirm termination', async () => { + const SECOND_SOCK = `${HOME}/.orca-remote/relay-0.1.0+cafebabe1234/${SOCK_NAME}` + const unconfirmed = Object.assign(new Error('channel close unconfirmed'), { + sshChannelCloseConfirmed: false + }) + const warn = warnSpy() + execCommand + .mockResolvedValueOnce(`${OLD_SOCK}\n${SECOND_SOCK}\n`) + .mockResolvedValueOnce(probe(['PRESENT=yes', 'LISTEN=unknown', 'HOLDERS_SOURCE=unavailable'])) + .mockRejectedValueOnce(unconfirmed) + + await expect(sweepSupersededRelayEndpoints(CONN, HOST, SWEEP)).rejects.toBe(unconfirmed) + + const logged = warn.mock.calls.flat().join('\n') + // Socket 1's verdict survives the abandon... + expect(logged).toContain('Superseded relay unverifiable') + expect(logged).toContain(OLD_SOCK) + // ...and the pass says how far it got, claiming nothing about the one it never reached. + expect(logged).toContain('stopped after 1 of 2 endpoints') + expect(logged).not.toContain(SECOND_SOCK) + }) + + // The loop must not stop on a probe that merely failed: that is an absence of evidence, and the + // remaining endpoints still deserve a pass. + it('carries on past an ordinary probe failure and classifies the rest', async () => { + const SECOND_SOCK = `${HOME}/.orca-remote/relay-0.1.0+cafebabe1234/${SOCK_NAME}` + execCommand + .mockResolvedValueOnce(`${OLD_SOCK}\n${SECOND_SOCK}\n`) + .mockRejectedValueOnce(new Error('probe blew up')) + .mockResolvedValueOnce(probe(['PRESENT=yes', 'LISTEN=unknown', 'HOLDERS_SOURCE=unavailable'])) + + const findings = await sweepSupersededRelayEndpoints(CONN, HOST, SWEEP) + + expect(findings.map((f) => f.outcome)).toEqual(['unverifiable', 'unverifiable']) + }) + it('does not run against Windows hosts, whose endpoints are named pipes', async () => { + const warn = warnSpy() await expect(sweepSupersededRelayEndpoints(CONN, WINDOWS_HOST, SWEEP)).resolves.toEqual([]) expect(execCommand).not.toHaveBeenCalled() + // The skip has to leave a trace: a Windows orphan is never listed and never reclaimed, and + // an empty return is otherwise indistinguishable from a host that had nothing to sweep. + const logged = warn.mock.calls.flat().join('\n') + expect(logged).toContain('Superseded relay sweep did not run') + expect(logged).toContain(CURRENT_DIR) }) }) diff --git a/src/main/ssh/ssh-relay-superseded-endpoints.ts b/src/main/ssh/ssh-relay-superseded-endpoints.ts index 4b1ad5637ef..ade50943c32 100644 --- a/src/main/ssh/ssh-relay-superseded-endpoints.ts +++ b/src/main/ssh/ssh-relay-superseded-endpoints.ts @@ -118,6 +118,17 @@ export async function sweepSupersededRelayEndpoints( options: SupersededRelaySweepOptions ): Promise { if (isWindowsRemoteHost(hostPlatform)) { + // No pass runs here: a Windows endpoint is a named pipe with no inode to stat, so the + // `$HOME` glob cannot see it, and `probeRelayEndpointIncumbent` answers `unverifiable` for + // every Windows path anyway — nothing on this host could be classified, let alone reaped. + // The population is real all the same (`relayEndpointForHost` hashes the version dir into + // the pipe name, so an update strands the incumbent exactly as it does on POSIX), and with + // `--grace-time 0` it keeps its PTYs forever. Returning silently was the whole bug: this + // sweep exists to make that population visible, and on Windows it made it invisible. + console.warn( + `[ssh-relay] Superseded relay sweep did not run (Windows named-pipe endpoints are not enumerated); ` + + `any orphan from an earlier build would be neither listed nor reclaimed: current=${options.currentRelayDir}` + ) return [] } let listing: string @@ -126,7 +137,14 @@ export async function sweepSupersededRelayEndpoints( wrapCommand: true, signal: options.signal }) - } catch { + } catch (err) { + // Same reason the Windows arm logs: an abandoned pass and an empty host are the same return + // value, and only the log tells them apart. + console.warn( + `[ssh-relay] Superseded relay listing failed; no pass ran: ${ + err instanceof Error ? err.message : String(err) + }` + ) return [] } const sockPaths = listing @@ -136,20 +154,35 @@ export async function sweepSupersededRelayEndpoints( .slice(0, MAX_SWEPT_ENDPOINTS) const findings: SupersededRelayFinding[] = [] - for (const sockPath of sockPaths) { - options.signal?.throwIfAborted() - const incumbent = await probeRelayEndpointIncumbent( - conn, - hostPlatform, - options.nodePath, - sockPath, - { signal: options.signal } + try { + for (const sockPath of sockPaths) { + options.signal?.throwIfAborted() + const incumbent = await probeRelayEndpointIncumbent( + conn, + hostPlatform, + options.nodePath, + sockPath, + { signal: options.signal } + ) + findings.push({ + sockPath, + outcome: await applySupersededRelayDecision(conn, incumbent, options), + incumbent + }) + } + } catch (err) { + // Why log before rethrowing: a probe or a reap that throws on socket 2 of N already classified + // socket 1, and those lines are the whole point of this pass. Dropping them made a half-run + // sweep read exactly like a host with nothing to sweep — the same defect the Windows arm above + // has, one level down. The throw still propagates unchanged; the caller separates + // RelayProbeCleanupUnconfirmedError from the rest. The count says how much of the pass ran, and + // claims nothing about the endpoints it never reached. + logSupersededRelayFindings(findings) + console.warn( + `[ssh-relay] Superseded relay sweep stopped after ${findings.length} of ${sockPaths.length} ` + + `endpoints; the rest were not examined: ${err instanceof Error ? err.message : String(err)}` ) - findings.push({ - sockPath, - outcome: await applySupersededRelayDecision(conn, incumbent, options), - incumbent - }) + throw err } logSupersededRelayFindings(findings) return findings From b6e039dec02abfd6ad6230d349a0134062097e4b Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 21:22:07 -0700 Subject: [PATCH 085/168] fix(settings): read host reachability from the shared verdict (#21206) Settings > Available Hosts and the repository host-setup section render the same host from the same store entry, but this row derived its own answer from raw `entry.status`. An unverifiable probe nulls that while the transport is still up, so the row flipped to "error" and swapped Disconnect for Connect while the other surface -- which already goes through runtimeHostConnectionStateForEntry -- still showed the host as reachable. One host, two surfaces, opposite answers. A probe that did not come back is not a host that went away. --- ...time-server-row-unverifiable-host.test.tsx | 129 ++++++++++++++++++ .../settings/runtime-server-row.tsx | 16 ++- 2 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 src/renderer/src/components/settings/runtime-server-row-unverifiable-host.test.tsx diff --git a/src/renderer/src/components/settings/runtime-server-row-unverifiable-host.test.tsx b/src/renderer/src/components/settings/runtime-server-row-unverifiable-host.test.tsx new file mode 100644 index 00000000000..6a1ba57dd0e --- /dev/null +++ b/src/renderer/src/components/settings/runtime-server-row-unverifiable-host.test.tsx @@ -0,0 +1,129 @@ +// @vitest-environment happy-dom + +import { cleanup, render, screen } from '@testing-library/react' +import { afterEach, beforeEach, expect, it, vi } from 'vitest' +import { + MIN_COMPATIBLE_RUNTIME_CLIENT_VERSION, + RUNTIME_PROTOCOL_VERSION +} from '../../../../shared/protocol-version' +import type { + RuntimeEnvironmentStatus, + RuntimeHostStatusSnapshot +} from '../../../../shared/runtime-host-status' +import type { PublicKnownRuntimeEnvironment } from '../../../../shared/runtime-environments' +import type { RuntimeStatus } from '../../../../shared/runtime-types' +import { useAppStore } from '@/store' +import { RuntimeServerRow } from './runtime-server-row' + +const ENVIRONMENT_ID = 'env-a' +const initialState = useAppStore.getInitialState() + +const environment: PublicKnownRuntimeEnvironment = { + id: ENVIRONMENT_ID, + name: 'Windows box', + createdAt: 100, + updatedAt: 100, + pairingRevision: 1, + lastUsedAt: null, + runtimeId: null, + endpoints: [{ id: 'ws-a', kind: 'websocket', label: 'WebSocket', endpoint: 'ws://x' }], + preferredEndpointId: 'ws-a' +} + +function answeredStatus(): RuntimeStatus { + return { + runtimeId: 'rt-1', + rendererGraphEpoch: 0, + graphStatus: 'ready', + authoritativeWindowId: 1, + liveTabCount: 0, + liveLeafCount: 0, + // Why real versions: an omitted protocol version is a compat block, which is its own + // disconnected verdict and would mask what this file is measuring. + runtimeProtocolVersion: RUNTIME_PROTOCOL_VERSION, + minCompatibleRuntimeClientVersion: MIN_COMPATIBLE_RUNTIME_CLIENT_VERSION + } +} + +function snapshot(patch: Partial): RuntimeHostStatusSnapshot { + return { + environmentId: ENVIRONMENT_ID, + pairingRevision: 1, + sequence: 2, + checkedAt: 2, + status: answeredStatus(), + verification: 'verified', + transport: 'ready', + ...patch + } +} + +function setEntry(entry: RuntimeEnvironmentStatus): void { + useAppStore.setState({ runtimeStatusByEnvironmentId: new Map([[ENVIRONMENT_ID, entry]]) }) +} + +function renderRow(): void { + render( + + ) +} + +beforeEach(() => { + useAppStore.setState(initialState, true) +}) + +afterEach(() => { + cleanup() + useAppStore.setState(initialState, true) +}) + +// Settings > Available Hosts and the repository host-setup section render the same host from the +// same entry. This row derived its own answer from raw `entry.status`, so a probe that did not +// come back flipped it to "error" and swapped Disconnect for Connect while the other surface, +// which already reads the shared verdict, still showed the host as reachable. +it('keeps offering Disconnect while a ready host answers an unverifiable probe', () => { + setEntry({ + status: null, + checkedAt: 2, + snapshot: snapshot({ verification: 'unavailable' }) + }) + renderRow() + + expect(screen.queryByRole('button', { name: /disconnect/i })).not.toBeNull() + expect(screen.queryByRole('button', { name: /^connect$/i })).toBeNull() +}) + +// The other direction must still work: a transport the host actually dropped is a host you +// reconnect to, and the row has to offer that. +it('offers Connect once the transport itself is down', () => { + setEntry({ + status: null, + checkedAt: 2, + snapshot: snapshot({ verification: 'unavailable', transport: 'disconnected' }) + }) + renderRow() + + expect(screen.queryByRole('button', { name: /disconnect/i })).toBeNull() +}) + +it('still offers Disconnect for a verified host', () => { + setEntry({ status: answeredStatus(), checkedAt: 2, snapshot: snapshot({}) }) + renderRow() + + expect(screen.queryByRole('button', { name: /disconnect/i })).not.toBeNull() +}) diff --git a/src/renderer/src/components/settings/runtime-server-row.tsx b/src/renderer/src/components/settings/runtime-server-row.tsx index b87b978d9ec..989e6a67d54 100644 --- a/src/renderer/src/components/settings/runtime-server-row.tsx +++ b/src/renderer/src/components/settings/runtime-server-row.tsx @@ -3,6 +3,10 @@ import type { PublicKnownRuntimeEnvironment } from '../../../../shared/runtime-e import type { RemoteServerUpdateEntry } from '@/runtime/remote-server-update-coordinator' import { translate } from '@/i18n/i18n' import { cn } from '@/lib/utils' +import { + isConnectedRuntimeHostState, + runtimeHostConnectionStateForEntry +} from '@/runtime/runtime-host-connection-state' import { useAppStore } from '@/store' import { Button } from '../ui/button' import { @@ -56,15 +60,23 @@ export function RuntimeServerRow({ const runtimeStatusEntry = useAppStore((state) => state.runtimeStatusByEnvironmentId.get(environment.id) ) + // Why the shared verdict and not `entry.status`: an unverifiable probe nulls it while the + // transport is still up, and this row then read "error" and offered Connect for a host that + // RepositoryHostSetupsSection -- which already derives through this same function -- was + // showing as reachable. One host, two surfaces, opposite answers. A probe that did not come + // back is not a host that went away (docs/reference/ssh-execution-boundary.md). + const entryReachable = + runtimeStatusEntry !== undefined && + isConnectedRuntimeHostState(runtimeHostConnectionStateForEntry(runtimeStatusEntry)) const effectiveDetails = runtimeStatusEntry ? { ...(details ?? { - status: runtimeStatusEntry.status ? ('ready' as const) : ('error' as const), + status: entryReachable ? ('ready' as const) : ('error' as const), runtimeStatus: null, compatibility: null, error: null }), - status: runtimeStatusEntry.status ? ('ready' as const) : ('error' as const), + status: entryReachable ? ('ready' as const) : ('error' as const), runtimeStatus: runtimeStatusEntry.status, compatibility: runtimeStatusEntry.status ? evaluateHostDetails(runtimeStatusEntry.status) From 355757c9477b86f82b9544f44cd4432e508efc9c Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 21:22:23 -0700 Subject: [PATCH 086/168] fix(terminal): keep the host's platform through an unverifiable probe (#21188) The platform a host runs is a fact about the host, not about whether its last probe came back. Reading `entry.status` fell through to the client's platform the moment a probe went unverifiable, so a Windows host driven from a Mac silently started resolving keystrokes and paths with POSIX conventions mid-session -- and switched back on the next successful probe. Same conversion as the four sibling reads, using the same shared reader. --- .../terminal-input-host-platform.test.ts | 55 +++++++++++++++++++ .../terminal-input-host-platform.ts | 12 ++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/renderer/src/components/terminal-pane/terminal-input-host-platform.test.ts b/src/renderer/src/components/terminal-pane/terminal-input-host-platform.test.ts index 5455618ee3c..da672176ee8 100644 --- a/src/renderer/src/components/terminal-pane/terminal-input-host-platform.test.ts +++ b/src/renderer/src/components/terminal-pane/terminal-input-host-platform.test.ts @@ -1,4 +1,6 @@ import { describe, expect, it } from 'vitest' +import type { RuntimeStatus } from '../../../../shared/runtime-types' +import type { RuntimeEnvironmentStatus } from '../../../../shared/runtime-host-status' import type { AppState } from '@/store/types' import { resolveTerminalInputHostPlatform } from './terminal-input-host-platform' @@ -18,6 +20,32 @@ function state(overrides: Partial = {}): AppState { } as AppState } +/** A Windows host that answered once and whose latest probe came back unverifiable. */ +function unverifiableWindowsHost(): RuntimeEnvironmentStatus { + const answered: RuntimeStatus = { + runtimeId: 'rt-win', + rendererGraphEpoch: 0, + graphStatus: 'ready', + authoritativeWindowId: null, + liveTabCount: 0, + liveLeafCount: 0, + hostPlatform: 'win32' + } + return { + status: null, + checkedAt: 2, + snapshot: { + environmentId: 'windows-box', + pairingRevision: 1, + sequence: 2, + checkedAt: 2, + status: answered, + verification: 'unavailable', + transport: 'ready' + } + } +} + describe('resolveTerminalInputHostPlatform', () => { it('uses a paired runtime host platform instead of the macOS client', () => { const worktreeId = 'repo::C:\\repo' @@ -280,6 +308,33 @@ describe('resolveTerminalInputHostPlatform', () => { ).toBe('win32') }) + // A probe that did not come back says nothing about which OS the host runs. Falling through to + // the client's platform re-points every keystroke and every path at the wrong conventions -- + // a Windows host driven from a Mac silently starts speaking POSIX mid-session. + it('keeps the Windows host platform while its probe is unverifiable', () => { + const worktreeId = 'repo::C:\\repo' + expect( + resolveTerminalInputHostPlatform({ + clientPlatform: 'darwin', + state: state({ + repos: [ + { + id: 'repo', + path: 'C:\\repo', + displayName: 'repo', + badgeColor: '#000', + addedAt: 0, + executionHostId: 'runtime:windows-box' + } + ], + runtimeStatusByEnvironmentId: new Map([['windows-box', unverifiableWindowsHost()]]) + }), + worktreeId, + transport: null + }) + ).toBe('win32') + }) + it('keeps the client platform for local terminals', () => { expect( resolveTerminalInputHostPlatform({ diff --git a/src/renderer/src/components/terminal-pane/terminal-input-host-platform.ts b/src/renderer/src/components/terminal-pane/terminal-input-host-platform.ts index 5758f41604a..2faaad7a117 100644 --- a/src/renderer/src/components/terminal-pane/terminal-input-host-platform.ts +++ b/src/renderer/src/components/terminal-pane/terminal-input-host-platform.ts @@ -1,4 +1,5 @@ import { parseExecutionHostId } from '../../../../shared/execution-host' +import { lastVerifiedRuntimeStatus } from '../../../../shared/runtime-host-status' import { isWslUncPath } from '../../../../shared/wsl-paths' import { getConnectionIdFromState } from '@/lib/connection-context' import { @@ -71,9 +72,12 @@ export function resolveTerminalInputHostPlatform(args: { ) } if (runtimeEnvironmentId) { + // Why last-verified: the host's platform is a fact about the host, and falling back to the + // client's silently re-points every keystroke and path at the wrong conventions -- a Windows + // host driven from a Mac. See docs/reference/ssh-execution-boundary.md. return ( - args.state.runtimeStatusByEnvironmentId.get(runtimeEnvironmentId)?.status?.hostPlatform ?? - args.clientPlatform + lastVerifiedRuntimeStatus(args.state.runtimeStatusByEnvironmentId.get(runtimeEnvironmentId)) + ?.hostPlatform ?? args.clientPlatform ) } const localSessionMetadata = args.transport?.getLocalSessionMetadata?.() @@ -95,8 +99,8 @@ export function resolveTerminalInputHostPlatform(args: { } if (host?.kind === 'runtime') { return ( - args.state.runtimeStatusByEnvironmentId.get(host.environmentId)?.status?.hostPlatform ?? - args.clientPlatform + lastVerifiedRuntimeStatus(args.state.runtimeStatusByEnvironmentId.get(host.environmentId)) + ?.hostPlatform ?? args.clientPlatform ) } return args.clientPlatform From a61119ceb0f21ba5a9b5a6fc7e08d94d295824de Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 21:22:39 -0700 Subject: [PATCH 087/168] refactor(runtime): name the four answers a host probe can give (#21207) The renderer expressed every non-answer as one nullable `status`, so a probe in flight, a probe that failed, a host that refused us and a retired pairing all reached readers as the same `null` -- and readers spent that `null` on decisions of very different weight, including destructive ones. `RuntimeHostContact` names the four. Nothing changes yet: the connection-state derivation is rewritten on top of it and a 384-case parity table asserts the result is identical to a frozen copy of the old one on every combination of verification, transport, retired, answered and remote-control state. --- docs/reference/ssh-execution-boundary.md | 27 ++ .../runtime/runtime-host-connection-state.ts | 30 ++- .../runtime-host-contact-parity.test.ts | 239 ++++++++++++++++++ src/shared/runtime-host-contact.ts | 118 +++++++++ 4 files changed, 401 insertions(+), 13 deletions(-) create mode 100644 src/renderer/src/runtime/runtime-host-contact-parity.test.ts create mode 100644 src/shared/runtime-host-contact.ts diff --git a/docs/reference/ssh-execution-boundary.md b/docs/reference/ssh-execution-boundary.md index 88a4a3c0a0e..a113a5e6f29 100644 --- a/docs/reference/ssh-execution-boundary.md +++ b/docs/reference/ssh-execution-boundary.md @@ -72,6 +72,33 @@ A verdict needs evidence from the host that owns the process. Apply these tests Anything short of positive host evidence is `unverifiable`. Reporting it as `exited` is the error this document exists to prevent: it orphans live work and can cold-start a duplicate over the same worktree. +## Host contact is a different question from process liveness + +The `live` / `unverifiable` / `exited` triple above answers one question: is this PTY running. It has +no synonyms, and nothing below adds any. + +A second, narrower question — can we currently reach the host at all, and what is its last answer +worth — is answered by `RuntimeHostContact` (`src/shared/runtime-host-contact.ts`), whose arms are +`live` / `unverifiable` / `refused` / `retired`. These are **not** extra process verdicts and must +never be mapped onto one: + +- `refused` is the host answering and turning us away — unauthorized, a protocol mismatch, a status + method it does not implement. That is positive evidence about the *connection*, and it says + nothing whatever about whether the host's PTYs are running. They almost certainly still are. +- `retired` is the pairing being ended by explicit user action. Same point: the client stops having + a route, the remote work is unaffected. + +Both are reasons to stop *trusting a cached answer*, never reasons to report a process `exited`. A +reader that needs a process verdict must still get it from the host that owns the process, by the +tests above. + +Why the extra arms exist at all: the renderer previously expressed every non-answer as one nullable +`status`, so a probe in flight, a probe that failed, a host that refused us and a retired pairing +all reached readers as the same `null` — and readers spent that `null` on decisions of very +different weight, including destructive ones. Folding `refused` and `retired` back into +`unverifiable` to match this document's triple would recreate exactly that collapse. The vocabularies +are deliberately separate because the questions are. + ## Deciding a remote pane is idle The orphan-PTY sweep is the one flow that turns an observation into a SIGKILL, so its idleness evidence has to be measured against the same thing the signal reaches. It is not the terminal. diff --git a/src/renderer/src/runtime/runtime-host-connection-state.ts b/src/renderer/src/runtime/runtime-host-connection-state.ts index 21e386857e9..4e5b99b916a 100644 --- a/src/renderer/src/runtime/runtime-host-connection-state.ts +++ b/src/renderer/src/runtime/runtime-host-connection-state.ts @@ -1,7 +1,5 @@ -import { - isRuntimeHostContactRevoked, - type RuntimeHostStatusSnapshot -} from '../../../shared/runtime-host-status' +import { runtimeHostContactFromSnapshot } from '../../../shared/runtime-host-contact' +import type { RuntimeHostStatusSnapshot } from '../../../shared/runtime-host-status' import type { RuntimeStatus } from '../../../shared/runtime-types' import { isRuntimeWorkspaceWindowClosed } from '../../../shared/runtime-workspace-window-availability' @@ -123,17 +121,23 @@ export function runtimeHostConnectionStateForEntry( ): RuntimeHostConnectionState { const snapshot = entry?.snapshot if (snapshot) { - if (isRuntimeHostContactRevoked(entry)) { + // Why the contact and not the snapshot fields: these four branches were the only place that + // knew a non-verified probe has kinds, and every other reader had to re-derive them or guess. + // Naming them once means the next reader picks an arm instead of re-reading a null. + const contact = runtimeHostContactFromSnapshot(snapshot, entry?.status ?? null) + if (contact.verdict === 'retired' || contact.verdict === 'refused') { return 'disconnected' } - if (snapshot.transport === 'disconnected') { - return 'reconnecting' - } - if (snapshot.verification === 'checking' && !entry?.status) { - return 'checking' - } - if (snapshot.transport === 'ready' && snapshot.verification !== 'verified') { - return 'runtime-unavailable' + if (contact.verdict === 'unverifiable') { + if (contact.reason === 'transport-down') { + return 'reconnecting' + } + if (contact.reason === 'checking') { + return 'checking' + } + if (contact.reason === 'probe-failed') { + return 'runtime-unavailable' + } } } return runtimeHostConnectionState({ diff --git a/src/renderer/src/runtime/runtime-host-contact-parity.test.ts b/src/renderer/src/runtime/runtime-host-contact-parity.test.ts new file mode 100644 index 00000000000..a24e963a1d9 --- /dev/null +++ b/src/renderer/src/runtime/runtime-host-contact-parity.test.ts @@ -0,0 +1,239 @@ +import { describe, expect, it } from 'vitest' +import { + isRuntimeHostContactRevoked, + type RuntimeHostStatusSnapshot +} from '../../../shared/runtime-host-status' +import { + isRuntimeHostContactRevokedVerdict, + lastRuntimeHostAnswer, + liveRuntimeHostStatus, + runtimeHostContactFromSnapshot +} from '../../../shared/runtime-host-contact' +import type { RuntimeStatus } from '../../../shared/runtime-types' +import { + runtimeHostConnectionState, + runtimeHostConnectionStateForEntry, + type RuntimeHostConnectionState +} from './runtime-host-connection-state' + +// This file exists to prove the contact introduced here changes nothing. It carries a frozen copy +// of the derivation as it stood before, and asserts the shipping one agrees with it on every +// combination of the inputs it reads. A behaviour change would have to survive the whole +// cross-product to go unnoticed, which is a much harder thing to do by accident than to argue. + +type Entry = { + status: RuntimeStatus | null + remoteControl?: RuntimeStatus['remoteControl'] | null + snapshot?: RuntimeHostStatusSnapshot +} + +/** The derivation exactly as it read before `RuntimeHostContact` existed. Do not refactor. */ +function legacyRuntimeHostConnectionStateForEntry( + entry: Entry | null | undefined +): RuntimeHostConnectionState { + const snapshot = entry?.snapshot + if (snapshot) { + if (snapshot.retired || snapshot.verification === 'blocked') { + return 'disconnected' + } + if (snapshot.transport === 'disconnected') { + return 'reconnecting' + } + if (snapshot.verification === 'checking' && !entry?.status) { + return 'checking' + } + if (snapshot.transport === 'ready' && snapshot.verification !== 'verified') { + return 'runtime-unavailable' + } + } + return runtimeHostConnectionState({ + hasStatusEntry: Boolean(entry), + status: entry?.status ?? null, + ...(snapshot?.transport === 'connecting' ? { transportStatus: 'checking' as const } : {}), + remoteControl: entry?.remoteControl ?? entry?.status?.remoteControl ?? null + }) +} + +const VERIFICATIONS = ['checking', 'verified', 'unavailable', 'blocked'] as const +const TRANSPORTS = ['unknown', 'connecting', 'ready', 'disconnected'] as const +const RETIRED = [false, true] as const +const REMOTE_CONTROL_STATES = [ + undefined, + 'ready', + 'awaiting_ready', + 'awaiting_authenticated', + 'reconnecting', + 'closed' +] as const + +function makeStatus(overrides: Partial = {}): RuntimeStatus { + return { + runtimeId: 'rt-1', + rendererGraphEpoch: 0, + graphStatus: 'ready', + authoritativeWindowId: 1, + liveTabCount: 0, + liveLeafCount: 0, + ...overrides + } +} + +function makeRemoteControl( + state: Exclude<(typeof REMOTE_CONTROL_STATES)[number], undefined> +): NonNullable { + return { + state, + pendingRequestCount: 0, + subscriptionCount: 0, + reconnectAttempt: 0, + lastConnectedAt: null, + lastClose: null, + lastError: null + } +} + +function makeSnapshot( + verification: (typeof VERIFICATIONS)[number], + transport: (typeof TRANSPORTS)[number], + retired: boolean, + answered: RuntimeStatus | null +): RuntimeHostStatusSnapshot { + return { + environmentId: 'env-a', + pairingRevision: 1, + sequence: 1, + checkedAt: 1, + status: answered, + verification, + transport, + ...(retired ? { retired: true as const } : {}) + } +} + +/** Every entry shape the derivation can distinguish: 4 x 4 x 2, across each status/diagnostic. */ +function* everySnapshotEntry(): Generator<{ label: string; entry: Entry }> { + for (const verification of VERIFICATIONS) { + for (const transport of TRANSPORTS) { + for (const retired of RETIRED) { + for (const answered of [null, makeStatus()] as const) { + for (const remoteControlState of REMOTE_CONTROL_STATES) { + // The store nulls `status` for anything but a verified, unretired probe, so the two + // reachable pairings are the ones enumerated here rather than a free cross-product. + const entryStatus = verification === 'verified' && !retired ? answered : null + const remoteControl = remoteControlState + ? makeRemoteControl(remoteControlState) + : undefined + yield { + label: `${verification}/${transport}/retired=${retired}/answered=${answered !== null}/rc=${remoteControlState ?? 'none'}`, + entry: { + status: entryStatus, + ...(remoteControl ? { remoteControl } : {}), + snapshot: makeSnapshot(verification, transport, retired, answered) + } + } + } + } + } + } + } +} + +describe('the host contact changes no verdict', () => { + it('agrees with the frozen derivation on every snapshot combination', () => { + const cases = [...everySnapshotEntry()] + // Guard against the enumeration silently collapsing: 4 x 4 x 2 x 2 x 6. + expect(cases).toHaveLength(384) + const disagreements = cases + .map(({ label, entry }) => ({ + label, + now: runtimeHostConnectionStateForEntry(entry), + before: legacyRuntimeHostConnectionStateForEntry(entry) + })) + .filter(({ now, before }) => now !== before) + expect(disagreements).toEqual([]) + }) + + it('agrees for entries that carry no snapshot at all', () => { + const entries: (Entry | null | undefined)[] = [ + null, + undefined, + { status: null }, + { status: makeStatus() }, + { status: null, remoteControl: makeRemoteControl('closed') }, + { status: null, remoteControl: makeRemoteControl('ready') }, + { status: makeStatus({ remoteControl: makeRemoteControl('reconnecting') }) } + ] + for (const entry of entries) { + expect(runtimeHostConnectionStateForEntry(entry)).toBe( + legacyRuntimeHostConnectionStateForEntry(entry) + ) + } + }) + + it('keeps the revoked predicate and the contact verdict in step', () => { + for (const { label, entry } of everySnapshotEntry()) { + expect( + isRuntimeHostContactRevokedVerdict( + runtimeHostContactFromSnapshot(entry.snapshot!, entry.status) + ), + label + ).toBe(isRuntimeHostContactRevoked(entry)) + } + }) +}) + +describe('the contact separates what the host said from what it is worth', () => { + it('retains the host answer through every non-live verdict', () => { + const answered = makeStatus() + for (const [verification, transport, retired] of [ + ['unavailable', 'ready', false], + ['checking', 'connecting', false], + ['unavailable', 'disconnected', false], + ['blocked', 'ready', false], + ['verified', 'ready', true] + ] as const) { + const contact = runtimeHostContactFromSnapshot( + makeSnapshot(verification, transport, retired, answered), + null + ) + expect(contact.verdict, `${verification}/${transport}`).not.toBe('live') + // The fact the host gave us survives; only its currency is in question. + expect(lastRuntimeHostAnswer(contact)).toBe(answered) + expect(liveRuntimeHostStatus(contact)).toBeNull() + } + }) + + it('reports a verified probe as live and nothing else', () => { + const answered = makeStatus() + const contact = runtimeHostContactFromSnapshot( + makeSnapshot('verified', 'ready', false, answered), + answered + ) + expect(contact.verdict).toBe('live') + expect(liveRuntimeHostStatus(contact)).toBe(answered) + expect(lastRuntimeHostAnswer(contact)).toBe(answered) + }) + + it('tells a host that was never reached apart from a handshake in flight', () => { + // These collapsed into one `null` before, and they want opposite affordances: one should + // offer Connect, the other should not. + expect( + runtimeHostContactFromSnapshot(makeSnapshot('unavailable', 'unknown', false, null), null) + ).toEqual({ verdict: 'unverifiable', reason: 'never-asked', lastAnswer: null }) + expect( + runtimeHostContactFromSnapshot(makeSnapshot('unavailable', 'connecting', false, null), null) + ).toEqual({ verdict: 'unverifiable', reason: 'transport-connecting', lastAnswer: null }) + }) + + it('tells a refused host apart from a retired pairing', () => { + const answered = makeStatus() + expect( + runtimeHostContactFromSnapshot(makeSnapshot('blocked', 'ready', false, answered), null) + .verdict + ).toBe('refused') + expect( + runtimeHostContactFromSnapshot(makeSnapshot('verified', 'ready', true, answered), null) + .verdict + ).toBe('retired') + }) +}) diff --git a/src/shared/runtime-host-contact.ts b/src/shared/runtime-host-contact.ts new file mode 100644 index 00000000000..84b3dcd0171 --- /dev/null +++ b/src/shared/runtime-host-contact.ts @@ -0,0 +1,118 @@ +import type { RuntimeHostStatusSnapshot } from './runtime-host-status' +import type { RuntimeStatus } from './runtime-types' + +/** + * What a host's last probe is worth, kept apart from what the host actually said. + * + * The store's `status` field answers both questions with one nullable value, so a probe still in + * flight, a probe that failed, a host that refused us and a pairing that was retired all arrive at + * a reader as the same `null`. Readers then spend that `null` on decisions of very different + * weight. This names the four answers so the decision happens where the evidence is understood. + * + * `unverifiable` is never `exited` (docs/reference/ssh-execution-boundary.md). `refused` and + * `retired` are the only arms carrying positive evidence, and they are separate because they + * differ in kind: one is the host turning us away, the other is the pairing being ended. + */ +export type RuntimeHostContact = + | { verdict: 'live'; status: RuntimeStatus } + | { + verdict: 'unverifiable' + reason: RuntimeHostContactUnverifiableReason + lastAnswer: RuntimeStatus | null + } + | { verdict: 'refused'; lastAnswer: RuntimeStatus | null } + | { verdict: 'retired'; lastAnswer: RuntimeStatus | null } + +/** + * Why the host's current state is unknown. `never-asked` is the absence of any transport attempt, + * which is where an unreachable paired host permanently sits — distinct from a handshake in + * flight, and the reason it must stay actionable rather than spin. + */ +export type RuntimeHostContactUnverifiableReason = + | 'never-asked' + | 'checking' + | 'probe-failed' + | 'transport-connecting' + | 'transport-down' + +/** + * The host's last answer whatever the verdict, for facts that do not expire — its build's + * capabilities, its platform, its runtime id. Returns null only when the host never answered. + */ +export function lastRuntimeHostAnswer(contact: RuntimeHostContact): RuntimeStatus | null { + return contact.verdict === 'live' ? contact.status : contact.lastAnswer +} + +/** The answer only while it is current, for decisions that must not act on a stale fact. */ +export function liveRuntimeHostStatus(contact: RuntimeHostContact): RuntimeStatus | null { + return contact.verdict === 'live' ? contact.status : null +} + +/** True only for the host's own terminal verdicts — the one state that may withdraw a fact. */ +export function isRuntimeHostContactRevokedVerdict(contact: RuntimeHostContact): boolean { + return contact.verdict === 'refused' || contact.verdict === 'retired' +} + +/** + * Why the order matters: it is the order the connection-state derivation already used, and the + * parity suite pins every combination against it. Transport loss outranks a probe in flight + * because a dead socket explains the silence; a ready transport with a failed probe is the host + * being unreachable at the runtime layer, not at the network layer. + */ +export function runtimeHostContactFromSnapshot( + snapshot: RuntimeHostStatusSnapshot, + entryStatus: RuntimeStatus | null = snapshot.status +): RuntimeHostContact { + const lastAnswer = snapshot.status + if (snapshot.retired) { + return { verdict: 'retired', lastAnswer } + } + if (snapshot.verification === 'blocked') { + return { verdict: 'refused', lastAnswer } + } + if (snapshot.transport === 'disconnected') { + return { verdict: 'unverifiable', reason: 'transport-down', lastAnswer } + } + if (snapshot.verification === 'checking' && !entryStatus) { + return { verdict: 'unverifiable', reason: 'checking', lastAnswer } + } + if (snapshot.transport === 'ready' && snapshot.verification !== 'verified') { + return { verdict: 'unverifiable', reason: 'probe-failed', lastAnswer } + } + if (snapshot.verification === 'verified' && entryStatus) { + return { verdict: 'live', status: entryStatus } + } + if (snapshot.transport === 'connecting') { + return { verdict: 'unverifiable', reason: 'transport-connecting', lastAnswer } + } + return { verdict: 'unverifiable', reason: 'never-asked', lastAnswer } +} + +/** + * The contact for a recorded entry. A stored `contact` wins so a writer can state one the + * snapshot cannot express — a probe that threw before any snapshot existed, say — and the + * snapshot derivation is the fallback while writers are still being converted. + */ +export function runtimeHostContactForEntry( + entry: + | { + status: RuntimeStatus | null + contact?: RuntimeHostContact + snapshot?: RuntimeHostStatusSnapshot + } + | null + | undefined +): RuntimeHostContact { + if (!entry) { + return { verdict: 'unverifiable', reason: 'never-asked', lastAnswer: null } + } + if (entry.contact) { + return entry.contact + } + if (entry.snapshot) { + return runtimeHostContactFromSnapshot(entry.snapshot, entry.status) + } + return entry.status + ? { verdict: 'live', status: entry.status } + : { verdict: 'unverifiable', reason: 'probe-failed', lastAnswer: null } +} From 82ca89124b8d42d604ef15ee4fe5596926bf7a17 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Fri, 18 Sep 2026 00:34:42 -0400 Subject: [PATCH 088/168] fix(lint): exempt the descendant-sweep test shim from the module-mocking gate (#21362) #20642 and #20645 added src/main/daemon/mock-descendant-sweep.ts and src/relay/mock-descendant-sweep.ts: test-only side-effect modules whose whole body is one vi.mock, imported by 60 suites so mock PTY PIDs never reach the host process table. Their CI ran before the anti-slop gate landed, so main now fails `oxlint --config config/oxlint-anti-slop.json` on every PR's merge ref. File-scoped exemption, like the others in this config, because the root lint scan does not load the plugin and an inline directive would read back as unused. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- config/oxlint-anti-slop.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/config/oxlint-anti-slop.json b/config/oxlint-anti-slop.json index bba8588d949..7d379a9df4e 100644 --- a/config/oxlint-anti-slop.json +++ b/config/oxlint-anti-slop.json @@ -56,6 +56,15 @@ "anti-slop/no-module-mocking": "off" } }, + // mock-descendant-sweep.ts (daemon and relay) is a test-only side-effect shim: its whole body + // is one vi.mock that keeps mock PTY PIDs away from the host process table, and it exists so + // 60 suites do not each inline the same hoisted factory. It is never imported by product code. + { + "files": ["**/mock-descendant-sweep.ts"], + "rules": { + "anti-slop/no-module-mocking": "off" + } + }, // The exemptions below are file-scoped rather than inline `oxlint-disable` comments // because the root lint scan does not load this plugin, so an inline directive naming // an anti-slop rule always reads back as an unused directive there. From 660969d1919e49016c8f2aba01d60242008d0db4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 04:38:25 +0000 Subject: [PATCH 089/168] Update README downloads badge --- docs/assets/readme-downloads.svg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/assets/readme-downloads.svg b/docs/assets/readme-downloads.svg index 766bd1e4cad..20fd5d9c2f2 100644 --- a/docs/assets/readme-downloads.svg +++ b/docs/assets/readme-downloads.svg @@ -1,5 +1,5 @@ - - downloads: 61m + + downloads: 62m @@ -15,7 +15,7 @@ downloads downloads - 61m - 61m + 62m + 62m From 07e8c851b8b03651468459d9a63c285329e6e105 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 21:39:41 -0700 Subject: [PATCH 090/168] fix(editor): evict stale mirrored file tabs (#21363) --- .../editor/useEditorPanelContentState.ts | 3 +- .../useEditorPanelFileLoadRetry.test.tsx | 32 +++++++++++++++++++ .../editor/useEditorPanelFileLoadRetry.ts | 19 +++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/components/editor/useEditorPanelContentState.ts b/src/renderer/src/components/editor/useEditorPanelContentState.ts index ec1a0fc6b1b..93c33d0961c 100644 --- a/src/renderer/src/components/editor/useEditorPanelContentState.ts +++ b/src/renderer/src/components/editor/useEditorPanelContentState.ts @@ -1,6 +1,6 @@ import { useCallback, useLayoutEffect, useRef, useState } from 'react' import type { OpenFile } from '@/store/slices/editor' -import type { useAppStore } from '@/store' +import { useAppStore } from '@/store' import type { DiffContent, FileContent } from './editor-panel-content-types' import { useEditorPanelExternalContentEvents, @@ -194,6 +194,7 @@ export function useEditorPanelContentState({ fileLoadRetryAttemptsRef, loadFileContent, openFilesRef, + closeFile: useAppStore.getState().closeFile, setFileContents }) diff --git a/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.test.tsx b/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.test.tsx index 6a29017d654..fe40142c645 100644 --- a/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.test.tsx +++ b/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.test.tsx @@ -49,6 +49,7 @@ function Harness({ attemptsRef, isVisible = true, loadFileContent, + closeFile = vi.fn(), setFileContents }: { file: OpenFile @@ -56,6 +57,7 @@ function Harness({ attemptsRef: { current: Record } isVisible?: boolean loadFileContent: (filePath: string, id: string) => Promise + closeFile?: (fileId: string) => void setFileContents: ( updater: (prev: Record) => Record ) => void @@ -66,6 +68,7 @@ function Harness({ fileLoadRetryAttemptsRef: attemptsRef, loadFileContent: loadFileContent as never, openFilesRef: { current: [file] }, + closeFile, setFileContents: setFileContents as never }) return null @@ -103,6 +106,35 @@ describe('useEditorPanelFileLoadRetry — owner-not-ready bounding (#6648)', () expect(shouldRetryFileLoadError('Access denied: outside allowed directories')).toBe(false) }) + it('evicts a mirrored tab after selector resolution stays missing', () => { + const file = makeFile({ mirroredFromRuntimeSession: true }) + const attemptsRef = { current: { [file.id]: 3 } } + const closeFile = vi.fn() + const fileContents: Record = { + [file.id]: { content: '', isBinary: false, loadError: 'selector_not_found' } + } + + container = document.createElement('div') + document.body.appendChild(container) + root = createRoot(container) + act(() => { + root?.render( + undefined)} + closeFile={closeFile} + setFileContents={(updater) => { + updater(fileContents) + }} + /> + ) + }) + + expect(closeFile).toHaveBeenCalledWith(file.id) + }) + it('does not spend retry budget when hiding cancels a pending retry', () => { setTimeoutSpy.mockRestore() setTimeoutSpy = vi.spyOn(window, 'setTimeout') diff --git a/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.ts b/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.ts index 9fb96ad80aa..57a9483218e 100644 --- a/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.ts +++ b/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.ts @@ -7,6 +7,7 @@ import { } from './editor-panel-content-types' const FILE_LOAD_RETRY_DELAYS_MS = [250, 1000, 2500] +const noopCloseFile = (): void => {} // Why: a remote host can take a while to finish connecting. The owner-not-ready // check is a pure local store read (it throws before any network call until the // SSH repo hydrates), so poll it at a steady cadence — but cap the wait so a @@ -30,9 +31,14 @@ type UseEditorPanelFileLoadRetryParams = { relativePath?: string ) => Promise openFilesRef: MutableRefObject + closeFile?: (fileId: string) => void setFileContents: Dispatch>> } +function isSelectorNotFoundError(message: string): boolean { + return message.trim().toLowerCase() === 'selector_not_found' +} + export function shouldRetryFileLoadError(message: string): boolean { // Terminal: the owner-not-ready budget is spent; only an explicit Retry should // restart it, never the automatic backoff. @@ -54,6 +60,7 @@ export function useEditorPanelFileLoadRetry({ fileLoadRetryAttemptsRef, loadFileContent, openFilesRef, + closeFile = noopCloseFile, setFileContents }: UseEditorPanelFileLoadRetryParams): void { const activeFileLoadRetryId = activeFile?.id ?? null @@ -75,6 +82,16 @@ export function useEditorPanelFileLoadRetry({ ? OWNER_NOT_READY_RETRY_LIMIT : FILE_LOAD_RETRY_DELAYS_MS.length if (retryCount >= retryLimit) { + if ( + !ownerNotReady && + isSelectorNotFoundError(activeFileLoadError) && + activeFile?.mirroredFromRuntimeSession === true + ) { + // A host-mirrored file whose worktree stays unresolvable after the normal + // read retries is stale; evict it before snapshots can select it again. + closeFile(activeFileLoadRetryId) + return + } // Why: the remote host never finished connecting. Replace the transient // "still connecting" text with a truthful terminal message so it does not // look like it is still retrying; Retry starts a fresh budget (#6648). @@ -126,6 +143,8 @@ export function useEditorPanelFileLoadRetry({ }, [ activeFileLoadRetryId, activeFileLoadError, + activeFile?.mirroredFromRuntimeSession, + closeFile, fileLoadRetryAttemptsRef, loadFileContent, openFilesRef, From 4b4ee040df75ea1c3f311347ff8c45cc9881ec07 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 21:50:15 -0700 Subject: [PATCH 091/168] perf(relay): index client request aborts instead of scanning every controller (#20052) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test(relay): measure per-connection teardown and hot-path costs by counting Both suites replace a would-be duration with the structural fact the duration was a proxy for, so neither depends on machine load. The census pins that attach/publish/detach churn returns every per-connection container to baseline, and asserts the containers actually filled first so a green cannot come from a probe that never loaded them. It also pins the one container with no per-client teardown: a publication-ledger entry is reclaimed only by its own lease, never by closeClient. The operation counts pin that notifyLegacyCapacity costs one ledger lookup per active client, that a broadcast costs a fixed number per subscriber, and that abortClient enumerates every controller rather than the target client's -- which is what makes a full client churn quadratic. * perf(relay): index client request aborts instead of scanning every controller abortClient runs on every closeClient and every setWrite. Under the flat map keyed `${clientId}:${requestId}` it had to walk every controller in the relay to find one client's, so a full churn of N clients each holding K in-flight requests cost K*N*(N+1)/2 key visits: measured 50 -> 5,100, 100 -> 20,200, 200 -> 80,400, 400 -> 320,800, exactly 4x per doubling. Do not "optimise" this back to a scan with an early break. It cannot work: the matching keys are scattered through the map, so any correct loop still visits every entry before it can know it is done. Only an index makes teardown proportional to what the client owns. `create` now returns an opaque handle carrying the owner, so a release finds its bucket without parsing a composite string key, and no call site changes. Also stop building the low-water key array eagerly. `belowLowWater` decides on the aggregate ceiling first and returns without reading the keys, but the caller had already allocated an N-element array and N template strings to pass them -- paying most in the loaded case, which is when that short-circuit fires. It takes a thunk now. The hot-path test becomes a guard rather than a characterisation: it asserts a teardown visits only the target client's K controllers and never enumerates the client index at all, since enumerating it is the old scan. Verified by mutation: restoring the scan shape fails it with "expected 40 to be +0". It asserts the maps really hold 160 controllers first, so it cannot pass by never filling them. * test(relay): make the capacity-thunk guard fail when the thunk is removed The operation-count test measured an idle dispatcher, where the aggregate ceiling never short-circuits, so every key is read whichever call shape is used. Reverting the thunk left all five assertions green -- it guarded nothing it claimed to. Adds the loaded arm, where the ceiling answers first and the saving exists, and asserts the client index is not enumerated at all. Reverting the thunk now fails it with `expected 50 to be +0`. Drops the ledger-retention case: it asserted a stranded entry SURVIVES close, so it pinned a capacity leak as a contract and would have broken whoever fixed it. It also used a key no client-keyed reclamation could match, and touched nothing this branch changes. The churn census already proves normal closes settle every entry; the gap is recorded there as a gap. * test(relay): carry the SAFETY: rationale main's casting gate now requires Not introduced here: main gained a `typescript/consistent-type-assertions` scan while this branch sat 432 commits behind, and every `as` in the two probe files this branch adds is new relative to main, so all 11 land as new findings. Verified by running the gate on this branch with and without my earlier test commit — 11 either way. Both files reach past `protected` to count containers, which is the measurement; each cast now carries the line-specific rationale AGENTS.md mandates. * test(relay): put the countingIterator SAFETY: directive on the line oxlint flags The diagnostic points at the `return {` that opens the object literal, not at the `} as IterableIterator` that closes it, so disable-next-line has to sit above the statement. * test(relay): type countingIterator as MapIterator and drop two suppressions The wrapper only ever receives a Map iterator, so declaring that removes the cast at both call sites; one irreducible cast stays on the object literal, which cannot satisfy MapIterator's full surface. Three suppressions become one. * fix(relay): key the abort index by the id's string form so a string id can still be cancelled The flat map's template key folded a request id of 7 and "7" onto one entry; keying the raw value split them, so rpc.cancel (which coerces through Number) missed a string-id request. Restore the coercion at the index. --- src/relay/client-request-aborts.ts | 78 +++++-- src/relay/dispatcher-capacity-signals.ts | 4 +- ...cher-per-connection-state-baseline.test.ts | 107 +++++++++ .../dispatcher-rpc-cancel-id-coercion.test.ts | 43 ++++ src/relay/legacy-relay-publication-ledger.ts | 11 +- .../relay-hot-path-operation-counts.test.ts | 205 ++++++++++++++++++ 6 files changed, 422 insertions(+), 26 deletions(-) create mode 100644 src/relay/dispatcher-per-connection-state-baseline.test.ts create mode 100644 src/relay/dispatcher-rpc-cancel-id-coercion.test.ts create mode 100644 src/relay/relay-hot-path-operation-counts.test.ts diff --git a/src/relay/client-request-aborts.ts b/src/relay/client-request-aborts.ts index 12d44363d0d..c896fb446b4 100644 --- a/src/relay/client-request-aborts.ts +++ b/src/relay/client-request-aborts.ts @@ -1,40 +1,74 @@ -export class ClientRequestAborts { - private readonly controllers = new Map() +/** Opaque handle returned by `create`, so a release needs no string parsing to find its owner. */ +export type ClientRequestAbortHandle = { + readonly clientId: number + readonly requestId: number +} - create(clientId: number, requestId: number): { key: string; controller: AbortController } { - const key = this.key(clientId, requestId) +export class ClientRequestAborts { + // Why indexed by client instead of one flat map under composite `${clientId}:${requestId}` keys: + // abortClient runs on every closeClient and every setWrite, and against a flat map it had to scan + // every entry to find one client's. A scan with an early break cannot fix that -- the matching + // keys are scattered through the map, so any correct loop still visits every entry, which made a + // full churn of N clients cost K*N*(N+1)/2 visits. Only an index makes a teardown proportional to + // what that client actually owns. + // + // Why the inner key is a string: the codec only checks `jsonrpc === '2.0'`, so a request `id` can + // arrive as `"7"` while `rpc.cancel` coerces its `id` through `Number(...)` and looks up `7`. The + // flat map's template key folded both onto `"7"`; keying the raw value would file them in + // different buckets and silently drop the cancel. `String(...)` is the template literal's coercion. + private readonly byClient = new Map>() + + create( + clientId: number, + requestId: number + ): { key: ClientRequestAbortHandle; controller: AbortController } { const controller = new AbortController() - this.controllers.set(key, controller) - return { key, controller } + let requests = this.byClient.get(clientId) + if (!requests) { + requests = new Map() + this.byClient.set(clientId, requests) + } + requests.set(String(requestId), controller) + return { key: { clientId, requestId }, controller } } get(clientId: number, requestId: number): AbortController | undefined { - return this.controllers.get(this.key(clientId, requestId)) + return this.byClient.get(clientId)?.get(String(requestId)) } - delete(key: string): void { - this.controllers.delete(key) + delete(key: ClientRequestAbortHandle): void { + const requests = this.byClient.get(key.clientId) + if (!requests) { + return + } + requests.delete(String(key.requestId)) + // Why drop the empty bucket: otherwise a churned client leaves an entry behind for the life of + // the relay, which is the retention the index exists to avoid. + if (requests.size === 0) { + this.byClient.delete(key.clientId) + } } abortClient(clientId: number): void { - const prefix = `${clientId}:` - for (const [key, controller] of this.controllers) { - if (!key.startsWith(prefix)) { - continue - } + const requests = this.byClient.get(clientId) + if (!requests) { + return + } + // Unlink before aborting: an abort listener that reaches back in must not see a half-emptied + // bucket, and the whole bucket is going regardless. + this.byClient.delete(clientId) + for (const controller of requests.values()) { controller.abort() - this.controllers.delete(key) } } abortAll(): void { - for (const [, controller] of this.controllers) { - controller.abort() + const buckets = Array.from(this.byClient.values()) + this.byClient.clear() + for (const requests of buckets) { + for (const controller of requests.values()) { + controller.abort() + } } - this.controllers.clear() - } - - private key(clientId: number, requestId: number): string { - return `${clientId}:${requestId}` } } diff --git a/src/relay/dispatcher-capacity-signals.ts b/src/relay/dispatcher-capacity-signals.ts index 5f945f5c84f..147db516041 100644 --- a/src/relay/dispatcher-capacity-signals.ts +++ b/src/relay/dispatcher-capacity-signals.ts @@ -55,7 +55,7 @@ export abstract class RelayDispatcherCapacitySignals extends RelayDispatcherClie } get legacyRetentionBelowLowWater(): boolean { - return this.publicationLedger.belowLowWater(this.activeClientKeys()) + return this.publicationLedger.belowLowWater(() => this.activeClientKeys()) } /** @@ -138,7 +138,7 @@ export abstract class RelayDispatcherCapacitySignals extends RelayDispatcherClie this.deferredLegacyCapacity ||= !force return } - if (!force && !this.publicationLedger.belowLowWater(this.activeClientKeys())) { + if (!force && !this.publicationLedger.belowLowWater(() => this.activeClientKeys())) { return } for (const listener of this.legacyCapacityListeners) { diff --git a/src/relay/dispatcher-per-connection-state-baseline.test.ts b/src/relay/dispatcher-per-connection-state-baseline.test.ts new file mode 100644 index 00000000000..70d5b325b71 --- /dev/null +++ b/src/relay/dispatcher-per-connection-state-baseline.test.ts @@ -0,0 +1,107 @@ +import { describe, expect, it, vi, afterEach } from 'vitest' +import { RelayDispatcher } from './dispatcher' + +// Why a census rather than a duration: "the dispatcher releases per-connection state" is a +// statement about what is still *held* after churn, so it is measured by counting containers, +// not by timing a teardown. Every number here is exact and load-independent. + +type Probed = { + attachClient: (w: (b: Buffer) => void) => number + detachClient: (id: number) => void + onClientCapacity: (id: number, listener: () => void) => (() => void) | null + clients: Map + requestHandlers: Map + notificationHandlers: Map + requestAborts: { + byClient: Map> + create: (clientId: number, requestId: number) => unknown + } + publicationLedger: { clientBytes: Map; aggregateBytes: number } + pendingRelayRequests: Map + clientDetachListeners: Set + disposeListeners: Set + legacyCapacityListeners: Set + clientCapacityListeners: Map + ptyDataPublicationAdmission: unknown + keepaliveTimer: unknown + activeClients: () => unknown[] + tryPublishToClients: (clients: unknown[], msg: unknown, lane: string) => boolean + dispose: () => void +} + +function countAbortControllers(d: Probed): number { + let total = 0 + for (const bucket of d.requestAborts.byClient.values()) { + total += bucket.size + } + return total +} + +function census(d: Probed): Record { + return { + clients: d.clients.size, + requestHandlers: d.requestHandlers.size, + notificationHandlers: d.notificationHandlers.size, + requestAbortControllers: countAbortControllers(d), + ledgerClientBytes: d.publicationLedger.clientBytes.size, + ledgerAggregateBytes: d.publicationLedger.aggregateBytes, + pendingRelayRequests: d.pendingRelayRequests.size, + clientDetachListeners: d.clientDetachListeners.size, + disposeListeners: d.disposeListeners.size, + legacyCapacityListeners: d.legacyCapacityListeners.size, + clientCapacityListeners: d.clientCapacityListeners.size, + ptyDataPublicationAdmission: d.ptyDataPublicationAdmission === null ? 'null' : 'set', + keepaliveTimer: d.keepaliveTimer === null ? 'null' : 'armed' + } +} + +function newDispatcher(): Probed { + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Probed names the protected containers this census counts. RelayDispatcher really has them; the compiler just will not hand them out. + return new RelayDispatcher(() => {}) as unknown as Probed +} + +const CLIENTS_PER_CYCLE = 100 + +describe('relay dispatcher per-connection state', () => { + afterEach(() => vi.useRealTimers()) + + it('returns every per-connection container to baseline across repeated churn', () => { + vi.useFakeTimers() + const d = newDispatcher() + const baseline = census(d) + + for (let cycle = 0; cycle < 3; cycle++) { + const ids: number[] = [] + for (let i = 0; i < CLIENTS_PER_CYCLE; i++) { + ids.push(d.attachClient(() => {})) + } + for (const id of ids) { + d.onClientCapacity(id, () => {}) + d.requestAborts.create(id, 1) + } + + // The ledger is the one container with no per-client teardown: an entry is reclaimed by its + // own lease's release(), never by closeClient. `ledgerClientBytes` returning to baseline + // below is therefore load-bearing -- it is the proof that normal closes settle every queued + // and in-flight entry. An entry that did somehow survive a close would not be reclaimed, and + // that is a gap to close, not a contract to pin. + // + // The census must be able to find things: these two are the containers that stay 0 unless + // deliberately loaded, so assert they actually moved before trusting that they came back. + expect(census(d).clients).toBe(CLIENTS_PER_CYCLE + 1) + expect(census(d).clientCapacityListeners).toBe(CLIENTS_PER_CYCLE) + expect(census(d).requestAbortControllers).toBe(CLIENTS_PER_CYCLE) + + d.tryPublishToClients( + d.activeClients(), + { jsonrpc: '2.0', method: 'pty.data', params: { d: 'x'.repeat(256) } }, + 'bulk' + ) + for (const id of ids) { + d.detachClient(id) + } + expect(census(d)).toEqual(baseline) + } + d.dispose() + }) +}) diff --git a/src/relay/dispatcher-rpc-cancel-id-coercion.test.ts b/src/relay/dispatcher-rpc-cancel-id-coercion.test.ts new file mode 100644 index 00000000000..9a015ea10c5 --- /dev/null +++ b/src/relay/dispatcher-rpc-cancel-id-coercion.test.ts @@ -0,0 +1,43 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { RelayDispatcher } from './dispatcher' +import { encodeFrame, MessageType } from './protocol' + +// Why both id shapes: parseJsonRpcMessage only checks the version, so a request id may arrive as a +// string, while rpc.cancel coerces its id through Number(...). The abort index must file both +// under one key or the cancel for a string-id request is silently dropped. +describe('rpc.cancel request-id coercion', () => { + let dispatcher: RelayDispatcher + + beforeEach(() => { + vi.useFakeTimers() + dispatcher = new RelayDispatcher(() => {}) + }) + + afterEach(() => { + dispatcher.dispose() + vi.useRealTimers() + }) + + it.each([ + { label: 'numeric', requestId: 7, cancelId: 7 }, + { label: 'string', requestId: '7', cancelId: '7' }, + { label: 'string request, numeric cancel', requestId: '7', cancelId: 7 } + ])('aborts an in-flight request with a $label id', async ({ requestId, cancelId }) => { + let signal: AbortSignal | undefined + dispatcher.onRequest('test.slow', (_params, ctx) => { + signal = ctx.signal + return new Promise(() => {}) + }) + // Raw frames: the typed encoder would not admit a string id, and that is the point. + const rawFrame = (msg: Record, seq: number): Buffer => + encodeFrame(MessageType.Regular, seq, 0, Buffer.from(JSON.stringify(msg), 'utf-8')) + + dispatcher.feed(rawFrame({ jsonrpc: '2.0', id: requestId, method: 'test.slow' }, 1)) + await vi.advanceTimersByTimeAsync(0) + expect(signal?.aborted).toBe(false) + + dispatcher.feed(rawFrame({ jsonrpc: '2.0', method: 'rpc.cancel', params: { id: cancelId } }, 2)) + + expect(signal?.aborted).toBe(true) + }) +}) diff --git a/src/relay/legacy-relay-publication-ledger.ts b/src/relay/legacy-relay-publication-ledger.ts index f7002756bc2..c641c9a0bd5 100644 --- a/src/relay/legacy-relay-publication-ledger.ts +++ b/src/relay/legacy-relay-publication-ledger.ts @@ -83,11 +83,18 @@ export class LegacyRelayPublicationLedger { }) } - belowLowWater(clientKeys?: readonly string[]): boolean { + // Why the thunk overload: the aggregate ceiling below decides on its own most of the time, and it + // decides *first*. A caller passing an eager array has already built one string per client before + // learning the keys were never going to be read -- and it pays that most in the loaded case, + // because that is exactly when the aggregate check short-circuits. + belowLowWater(clientKeys?: readonly string[] | (() => readonly string[])): boolean { if (this.aggregateBytes > this.relayLowBytes) { return false } - const keys = clientKeys ?? Array.from(this.clientBytes.keys()) + const keys = + typeof clientKeys === 'function' + ? clientKeys() + : (clientKeys ?? Array.from(this.clientBytes.keys())) return keys.every((clientKey) => (this.clientBytes.get(clientKey) ?? 0) <= this.clientLowBytes) } diff --git a/src/relay/relay-hot-path-operation-counts.test.ts b/src/relay/relay-hot-path-operation-counts.test.ts new file mode 100644 index 00000000000..c864f058cc5 --- /dev/null +++ b/src/relay/relay-hot-path-operation-counts.test.ts @@ -0,0 +1,205 @@ +import { describe, expect, it, vi, afterEach } from 'vitest' +import { RelayDispatcher } from './dispatcher' +import { ClientRequestAborts } from './client-request-aborts' + +// Why operation counts and not milliseconds: each assertion below is about how many entries a hot +// path visits, which is the property. A duration is only a proxy for it, and a proxy needs a +// threshold calibrated against observed runtimes -- which makes the test about the observation. +// These counts are exact and identical under any machine load. + +/** Counts entries yielded by a real Map's iterators without changing the code under test. */ +class CountingMap extends Map { + visits = 0 + getCalls = 0 + + private countingIterator(inner: MapIterator): MapIterator { + const bump = (): void => { + this.visits++ + } + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the literal returned here implements next() and [Symbol.iterator](), which is the whole protocol a for..of over this wrapper reaches; no other IterableIterator member is ever called. + return { + next(): IteratorResult { + const r = inner.next() + if (!r.done) { + bump() + } + return r + }, + [Symbol.iterator]() { + return this + } + } as MapIterator + } + + override [Symbol.iterator](): MapIterator<[K, V]> { + return this.countingIterator(super[Symbol.iterator]()) + } + + override values(): MapIterator { + return this.countingIterator(super.values()) + } + + override get(key: K): V | undefined { + this.getCalls++ + return super.get(key) + } +} + +type ProbedDispatcher = { + attachClient: (w: (b: Buffer) => void) => number + clients: Map + publicationLedger: { + clientBytes: Map + readonly retainedBytes: number + readonly relayLowBytes: number + readonly clientHighBytes: number + tryReserve: (m: readonly { clientKey: string; bytes: number }[]) => unknown[] | null + } + notifyLegacyCapacityIfLow: () => void + activeClients: () => unknown[] + activeClientKeys: () => string[] + tryPublishToClients: (clients: unknown[], msg: unknown, lane: string) => boolean + dispose: () => void +} + +/** Reserves through the real lease path until aggregate retention clears the relay low-water mark. */ +function loadLedgerAboveLowWater(d: ProbedDispatcher): void { + const ledger = d.publicationLedger + for (const clientKey of d.activeClientKeys()) { + if (ledger.retainedBytes > ledger.relayLowBytes) { + return + } + ledger.tryReserve([{ clientKey, bytes: ledger.clientHighBytes }]) + } +} + +function dispatcherWithClients(clientCount: number): { + d: ProbedDispatcher + clients: CountingMap + ledger: CountingMap +} { + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: ProbedDispatcher names the protected members this census reads. RelayDispatcher really has them; the compiler just will not hand them out. + const d = new RelayDispatcher(() => {}) as unknown as ProbedDispatcher + for (let i = 1; i < clientCount; i++) { + d.attachClient(() => {}) + } + const clients = new CountingMap() + for (const [k, v] of d.clients) { + clients.set(k, v) + } + d.clients = clients + const ledger = new CountingMap() + d.publicationLedger.clientBytes = ledger + clients.visits = 0 + ledger.getCalls = 0 + return { d, clients, ledger } +} + +describe('relay hot-path operation counts', () => { + afterEach(() => vi.useRealTimers()) + + // Why this is the guard and not a duration: abortClient runs on every closeClient and every + // setWrite. Under the flat composite-key map it replaced, one client's teardown enumerated every + // controller in the relay, so a full churn of N clients holding K requests cost K*N*(N+1)/2 visits + // -- measured at 50 -> 5,100, 100 -> 20,200, 200 -> 80,400, 400 -> 320,800, exactly 4x per + // doubling. Teardown must now visit only what the client owns, and must not enumerate the client + // index at all: enumerating it *is* the old scan. + it('abortClient visits only the target client, and never enumerates the client index', () => { + const clientCount = 40 + const inFlightPerClient = 4 + const aborts = new ClientRequestAborts() + for (let c = 1; c <= clientCount; c++) { + for (let r = 1; r <= inFlightPerClient; r++) { + aborts.create(c, r) + } + } + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: byClient is the private index this test exists to measure; the shape mirrors its declaration in client-request-aborts.ts. + const byClient = (aborts as unknown as { byClient: Map> }) + .byClient + + // The census must be able to find things: prove the maps really hold 160 controllers across 40 + // buckets before asserting that a teardown only touches 4 of them. + expect(byClient.size).toBe(clientCount) + let totalControllers = 0 + for (const bucket of byClient.values()) { + totalControllers += bucket.size + } + expect(totalControllers).toBe(clientCount * inFlightPerClient) + + const index = new CountingMap>() + for (const [k, v] of byClient) { + index.set(k, v) + } + const targetBucket = new CountingMap() + for (const [k, v] of byClient.get(1)!) { + targetBucket.set(k, v) + } + index.set(1, targetBucket) + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: swaps the counting stand-in into the same private index read above. + ;(aborts as unknown as { byClient: Map }).byClient = index + index.visits = 0 + targetBucket.visits = 0 + + aborts.abortClient(1) + + expect(targetBucket.visits).toBe(inFlightPerClient) + expect(index.visits).toBe(0) + expect(index.has(1)).toBe(false) + }) + + // Scope: this is the idle arm, where every key has to be read whatever the call shape is. It + // guards against a per-client lookup becoming a per-client scan; it does NOT guard the thunk -- + // the counts below are identical with and without it. The loaded arm is the next test. + it('notifyLegacyCapacity costs exactly one ledger lookup per active client when idle', () => { + vi.useFakeTimers() + for (const clientCount of [50, 100, 200, 400]) { + const { d, clients, ledger } = dispatcherWithClients(clientCount) + + d.notifyLegacyCapacityIfLow() + + expect(clients.visits, `clients enumerated at n=${clientCount}`).toBe(clientCount) + expect(ledger.getCalls, `ledger lookups at n=${clientCount}`).toBe(clientCount) + d.dispose() + } + }) + + // Why the loaded ledger is the one that measures the thunk: the aggregate ceiling answers first + // and on its own, so a caller passing an eager array has already built one key string per client + // before learning they were never going to be read. That is the whole saving, and it is invisible + // below the low-water mark -- which is why counting an idle dispatcher guards nothing. + it('does not enumerate clients at all once the aggregate ceiling answers', () => { + vi.useFakeTimers() + for (const clientCount of [50, 100, 200, 400]) { + const { d, clients, ledger } = dispatcherWithClients(clientCount) + loadLedgerAboveLowWater(d) + // The census must be able to find things: a reserve that silently failed would leave the + // ledger idle and make every count below pass for the wrong reason. + expect(d.publicationLedger.retainedBytes).toBeGreaterThan(d.publicationLedger.relayLowBytes) + clients.visits = 0 + ledger.getCalls = 0 + + d.notifyLegacyCapacityIfLow() + + expect(clients.visits, `clients enumerated at n=${clientCount}`).toBe(0) + expect(ledger.getCalls, `ledger lookups at n=${clientCount}`).toBe(0) + d.dispose() + } + }) + + it('one broadcast publication costs a fixed number of lookups per subscriber', () => { + vi.useFakeTimers() + for (const clientCount of [10, 20, 40]) { + const { d, clients, ledger } = dispatcherWithClients(clientCount) + + d.tryPublishToClients( + d.activeClients(), + { jsonrpc: '2.0', method: 'pty.data', params: { d: 'x' } }, + 'bulk' + ) + + expect(clients.visits, `clients enumerated at n=${clientCount}`).toBe(clientCount * 2) + expect(ledger.getCalls, `ledger lookups at n=${clientCount}`).toBe(clientCount * 4) + d.dispose() + } + }) +}) From 9641a1b5440e387c12108de7296f2d57d2ab0a6b Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:04:27 -0400 Subject: [PATCH 092/168] feat(mobile-web-bundle): serve the packaged mobile web bundle over RPC (OTA phase A, 3/5) (#21348) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(mobile-web-bundle): serve the bundle manifest and chunks over RPC Two paired-runtime methods on the already-authenticated connection: `mobileWeb.bundle.manifest` returns this install's manifest plus the chunk size it advertises, and `mobileWeb.bundle.chunk` returns one aligned range of one asset with the whole asset's length and hash, so a single chunk describes what it belongs to. `path` is accepted only by exact match against a manifest member, so traversal is unreachable rather than mitigated. Each asset's on-disk sha256 is verified once and the verdict remembered, concurrent first readers sharing one hash. Reads are capped at four in flight per connection, and a disconnected client stops costing reads at the next checkpoint. No SSH or relay proxying: a runtime answers only out of its own install. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile-web-bundle): pin the three buildId serializers against each other The canonical serialization exists in the builder, the packaging guard, and the shared contract, because the two packaging scripts run on bare node before any build output exists and cannot import TypeScript. A divergence in any one would reject every honest bundle at packaging, or ship a bundle whose id the phone recomputes differently and re-downloads forever. Proved red by swapping the guard's code-unit sort for localeCompare: five of six cases fail. Exports the guard's serializer for the test; no packaging behaviour changes. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile-web-bundle): cover every error code and a multi-chunk paging round trip Against a synthetic bundle in a temp dir, because the real builder's largest asset is under one chunk and CI unit jobs never build out/mobile-web. The fixture's script spans three chunks, its stylesheet is exactly one, and one asset is empty, so paging, the eof boundary, and the zero-byte case are exercised rather than assumed. Reads in flight are held by latching `open`, so the four-per-connection cap and an abort arriving mid-read are deterministic rather than a race with a stopwatch. Both were proved red: dropping the abort check after verification fails the abort case, and keying the cap on connectionId alone fails the device-token case. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web-bundle): resolve the bundle root through the AppEnvironment port check:runtime-electron-ratchet caught this: the resolver sat beside getBundledWebClientRoot in src/main/startup and imported electron, and importing it from an RPC method pulled the first electron edge into a runtime graph whose baseline is zero. The runtime has to stay bootable on plain Node. So it reads app.getAppPath() through the port every other runtime module already uses, and moves next to its two callers under src/main/runtime. A host with no environment installed has no install root, which is the same answer as having no bundle. orcad answers getAppPath from its own install root, so a headless runtime that carries the artifact serves it with no special case. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile-web-bundle): cover the resolver's two probe layouts directly Also stops exporting the manifest filename, which nothing outside the resolver needs. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile-web-bundle): pin both methods on the mobile allowlist The scanner only checks mobile-used ⊆ allowlist, and no mobile source calls these until A5, so deleting both entries left every test green. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web-bundle): keep filesystem failures inside the six error codes An asset unlinked or truncated after its verdict was cached reached the client as runtime_error carrying the desktop's absolute install path. Both now answer mobile_web_bundle_asset_changed, with the cause warned host-side only. A short positional read is the truncation case, so it throws instead of paging the client past the end. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-web-bundle): drop the unreachable release-idempotence guard The one caller releases exactly once in a finally; removing the flag left every test green, so it was defensiveness against a caller that does not exist. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile-web-bundle): prove a failed verify is not cached as a verdict The verdict cache never invalidates, so a transient read failure remembered as a verdict would poison the asset for the life of the process. Removing the delete left every test green until now. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-web-bundle): delete the unsatisfiable manifest params schema The dispatcher substitutes `{}` for absent params, so `z.null()` could never parse; the method declares `params: null` instead. A comment on the method name records why there is no schema. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile-web-bundle): fill the read window instead of failing a partial read fs.read may answer short of what it was asked for before EOF, so the previous check turned a legitimate partial read into a spurious asset_changed. The loop mirrors the relay's readFullStreamChunk, which is not imported because it sits behind the relay dispatcher's module graph; only a read returning nothing is treated as truncation. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-web-bundle): read the disconnect idiom with the shared predicate isClientDisconnectedError already exports exactly the check the catch needed, so the local error class goes away and the throw returns to the repo-wide idiom. The module doc now says asContractError is a total catch. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile-web-bundle): pin the four branches no test was holding Each one survived a mutation: the abort check before verification, the per-process manifest cache, the buildId component of the verdict key, and delete-at-zero in the admission map. The last two matter beyond hygiene — a verdict keyed by path alone carries a failed verdict onto the next build of index.html, and a map that never drops a key retains one pairing token per socket. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- ...bile-web-bundle-serializer-parity.test.mjs | 119 ++++ .../verify-packaged-mobile-web-bundle.cjs | 4 +- src/main/runtime/bundled-mobile-web-bundle.ts | 85 +++ src/main/runtime/rpc/methods/index.ts | 2 + .../methods/mobile-web-bundle-asset-reader.ts | 122 ++++ .../mobile-web-bundle-read-admission.ts | 45 ++ ...mobile-web-bundle-read-concurrency.test.ts | 253 ++++++++ .../methods/mobile-web-bundle.test-fixture.ts | 106 ++++ .../rpc/methods/mobile-web-bundle.test.ts | 552 ++++++++++++++++++ .../runtime/rpc/methods/mobile-web-bundle.ts | 145 +++++ .../runtime-rpc-mobile-method-allowlist.ts | 2 + .../bundle-rpc-contract.test.ts | 6 - .../mobile-web-bundle/bundle-rpc-contract.ts | 5 +- .../rpc-params-catalog.generated.ts | 3 + 14 files changed, 1439 insertions(+), 10 deletions(-) create mode 100644 config/scripts/mobile-web-bundle-serializer-parity.test.mjs create mode 100644 src/main/runtime/bundled-mobile-web-bundle.ts create mode 100644 src/main/runtime/rpc/methods/mobile-web-bundle-asset-reader.ts create mode 100644 src/main/runtime/rpc/methods/mobile-web-bundle-read-admission.ts create mode 100644 src/main/runtime/rpc/methods/mobile-web-bundle-read-concurrency.test.ts create mode 100644 src/main/runtime/rpc/methods/mobile-web-bundle.test-fixture.ts create mode 100644 src/main/runtime/rpc/methods/mobile-web-bundle.test.ts create mode 100644 src/main/runtime/rpc/methods/mobile-web-bundle.ts diff --git a/config/scripts/mobile-web-bundle-serializer-parity.test.mjs b/config/scripts/mobile-web-bundle-serializer-parity.test.mjs new file mode 100644 index 00000000000..9b82a09265b --- /dev/null +++ b/config/scripts/mobile-web-bundle-serializer-parity.test.mjs @@ -0,0 +1,119 @@ +/** + * The canonical serialization that buildId hashes exists three times, because the two packaging + * scripts run on bare node before any build output exists and so cannot import the TypeScript + * contract. Three copies drift; this is what stops them. A divergence in any one of them would + * reject every honest bundle at packaging, or ship a bundle whose id the phone recomputes + * differently and re-downloads forever. + */ +import { createHash } from 'node:crypto' +import { createRequire } from 'node:module' +import { describe, expect, it } from 'vitest' +import { + computeMobileWebBundleBuildId, + serializeMobileWebBundleAssets as serializeInBuilder +} from './build-mobile-web-bundle.mjs' +import { + computeMobileWebBundleId, + MobileWebBundleManifestSchema, + serializeMobileWebBundleAssets as serializeInContract +} from '../../src/shared/mobile-web-bundle/manifest-contract' + +const require = createRequire(import.meta.url) +const { serializeAssets: serializeInGuard } = require('./verify-packaged-mobile-web-bundle.cjs') + +const digest = (hex) => `${hex}`.padStart(64, '0') + +/** + * Mixed content types, a nested path, and an uppercase segment that sorts before a lowercase one + * only under code-unit order: `localeCompare` would put `assets/aQ.js` first, so any serializer + * that reached for it produces a different string here. + */ +const ASSETS = [ + { + path: 'assets/Za.js', + sha256: digest('a1'), + byteLength: 2048, + contentType: 'text/javascript; charset=utf-8' + }, + { path: 'assets/aQ.css', sha256: digest('b2'), byteLength: 512, contentType: 'text/css' }, + { + path: 'assets/nested/mark.png', + sha256: digest('c3'), + byteLength: 40_960, + contentType: 'image/png' + }, + { + path: 'index.html', + sha256: digest('d4'), + byteLength: 640, + contentType: 'text/html; charset=utf-8' + } +] + +const REORDERED = [ASSETS[3], ASSETS[1], ASSETS[0], ASSETS[2]] +const REVERSED = ASSETS.toReversed() + +const sha256Hex = (value) => createHash('sha256').update(value, 'utf8').digest('hex') + +describe('the three mobile web bundle serializers', () => { + it('produce one string for the builder, the packaging guard, and the shared contract', () => { + const fromContract = serializeInContract(ASSETS) + + expect(serializeInBuilder(ASSETS)).toBe(fromContract) + expect(serializeInGuard(ASSETS)).toBe(fromContract) + }) + + it.each([ + ['reordered', REORDERED], + ['reversed', REVERSED] + ])('are order-independent, so %s input serializes identically', (_label, input) => { + const expected = serializeInContract(ASSETS) + + expect(serializeInContract(input)).toBe(expected) + expect(serializeInBuilder(input)).toBe(expected) + expect(serializeInGuard(input)).toBe(expected) + }) + + it('leaves the caller-supplied array untouched, so a build cannot depend on the sort', () => { + const input = [...REORDERED] + serializeInContract(input) + serializeInBuilder(input) + serializeInGuard(input) + + expect(input).toEqual(REORDERED) + }) + + it('emit exactly path, sha256, byteLength, contentType, in that order, and nothing else', () => { + const decorated = ASSETS.map((asset) => ({ ...asset, sourcePath: '/tmp/ignored', extra: 1 })) + + expect(serializeInContract(decorated)).toBe(serializeInContract(ASSETS)) + expect(serializeInBuilder(decorated)).toBe(serializeInContract(ASSETS)) + expect(serializeInGuard(decorated)).toBe(serializeInContract(ASSETS)) + expect(JSON.parse(serializeInContract(ASSETS))[0]).toEqual({ + path: 'assets/Za.js', + sha256: digest('a1'), + byteLength: 2048, + contentType: 'text/javascript; charset=utf-8' + }) + }) + + it('hash to one buildId, which the manifest schema then accepts', () => { + const buildId = computeMobileWebBundleId(REORDERED) + + expect(computeMobileWebBundleBuildId(REORDERED)).toBe(buildId) + expect(sha256Hex(serializeInGuard(REORDERED))).toBe(buildId) + + const manifest = { + schemaVersion: 1, + buildId, + desktopVersion: '1.4.200', + minCompatibleRuntimeProtocolVersion: 2, + runtimeProtocolVersion: 2, + entrypoint: 'index.html', + totalBytes: ASSETS.reduce((total, asset) => total + asset.byteLength, 0), + assets: [...ASSETS] + } + + expect(MobileWebBundleManifestSchema.parse(manifest).buildId).toBe(buildId) + }) +}) diff --git a/config/scripts/verify-packaged-mobile-web-bundle.cjs b/config/scripts/verify-packaged-mobile-web-bundle.cjs index 13cf71b008b..c521882691d 100644 --- a/config/scripts/verify-packaged-mobile-web-bundle.cjs +++ b/config/scripts/verify-packaged-mobile-web-bundle.cjs @@ -188,4 +188,6 @@ function assertMobileWebBundleBuilt(bundleDir = MOBILE_WEB_BUNDLE_DIR) { return manifest } -module.exports = { MOBILE_WEB_BUNDLE_DIR, assertMobileWebBundleBuilt } +// serializeAssets is exported for the parity test that pins it against the builder's and the +// contract's serializers; nothing in packaging calls it from outside this module. +module.exports = { MOBILE_WEB_BUNDLE_DIR, assertMobileWebBundleBuilt, serializeAssets } diff --git a/src/main/runtime/bundled-mobile-web-bundle.ts b/src/main/runtime/bundled-mobile-web-bundle.ts new file mode 100644 index 00000000000..b3ffb06f912 --- /dev/null +++ b/src/main/runtime/bundled-mobile-web-bundle.ts @@ -0,0 +1,85 @@ +import { existsSync, readFileSync } from 'node:fs' +import { join } from 'node:path' +import { getAppEnvironment, hasAppEnvironment } from '../../shared/app-environment' +import { + MobileWebBundleManifestSchema, + type MobileWebBundleManifest +} from '../../shared/mobile-web-bundle/manifest-contract' + +const MANIFEST_FILENAME = 'manifest.json' + +export type BundledMobileWebBundle = { + root: string + manifest: MobileWebBundleManifest +} + +/** + * Probed exactly like getBundledWebClientRoot: the bundle ships inside app.asar under out/, so the + * two entrypoint layouts that move appPath are the only ones that can move it. + * + * Read through the AppEnvironment port rather than `electron.app`, because this module is reachable + * from the runtime's import graph and the runtime must stay bootable on plain Node. A host with no + * environment installed has no install root, which is the same answer as having no bundle. + */ +export function getBundledMobileWebBundleRoot(): string | undefined { + if (!hasAppEnvironment()) { + return undefined + } + const appPath = getAppEnvironment().getAppPath() + const roots = [ + join(appPath, 'out', 'mobile-web'), + // Why: unpacked electron-vite entrypoints set appPath to out/main, next to the bundle. + join(appPath, '..', 'mobile-web') + ] + return roots.find((root) => existsSync(join(root, MANIFEST_FILENAME))) +} + +// Why no invalidation: the bundle is immutable for the life of the install, and an auto-update +// replaces it only by restarting the app, so a stale entry cannot outlive the process that read it. +// `undefined` means "not looked at yet", `null` means "looked, and this install has no bundle". +let cachedBundle: BundledMobileWebBundle | null | undefined + +export function loadBundledMobileWebBundle(): BundledMobileWebBundle | null { + if (cachedBundle === undefined) { + cachedBundle = readBundledMobileWebBundle() + } + return cachedBundle +} + +/** Tests own the process, so they own the cache; nothing in the app may call this. */ +export function resetBundledMobileWebBundleCacheForTests(): void { + cachedBundle = undefined +} + +function readBundledMobileWebBundle(): BundledMobileWebBundle | null { + const root = getBundledMobileWebBundleRoot() + if (!root) { + return null + } + const manifestPath = join(root, MANIFEST_FILENAME) + let raw: string + try { + raw = readFileSync(manifestPath, 'utf8') + } catch (error) { + console.warn(`[mobile-web-bundle] cannot read ${manifestPath}:`, error) + return null + } + let parsed: unknown + try { + parsed = JSON.parse(raw) + } catch (error) { + console.warn(`[mobile-web-bundle] ${manifestPath} is not valid JSON:`, error) + return null + } + const manifest = MobileWebBundleManifestSchema.safeParse(parsed) + if (!manifest.success) { + // Why warn rather than throw: packaging already hash-verifies the bundle, so reaching here means + // a dev or hand-edited out/, and an unusable bundle must degrade to "no bundle", never to a + // crash on a path a phone can reach. + console.warn(`[mobile-web-bundle] ${manifestPath} does not match the manifest contract:`, { + issues: manifest.error.issues + }) + return null + } + return { root, manifest: manifest.data } +} diff --git a/src/main/runtime/rpc/methods/index.ts b/src/main/runtime/rpc/methods/index.ts index a247c8bbe41..cbf6e0d8e0c 100644 --- a/src/main/runtime/rpc/methods/index.ts +++ b/src/main/runtime/rpc/methods/index.ts @@ -38,6 +38,7 @@ import { PLUGIN_METHODS } from './plugins' import { SKILL_METHODS } from './skills' import { CLIPBOARD_METHODS } from './clipboard' import { HOST_CAPABILITY_METHODS } from './host-capabilities' +import { MOBILE_WEB_BUNDLE_METHODS } from './mobile-web-bundle' import { RUNTIME_CLIENT_CAPABILITY_METHODS } from './runtime-client-capabilities' import { EMULATOR_METHODS } from './emulator' import { PAIRING_METHODS } from './pairing' @@ -95,6 +96,7 @@ export const ALL_RPC_METHODS = [ ...SKILL_METHODS, ...CLIPBOARD_METHODS, ...HOST_CAPABILITY_METHODS, + ...MOBILE_WEB_BUNDLE_METHODS, ...RUNTIME_CLIENT_CAPABILITY_METHODS, ...CLIENT_EVENT_METHODS, ...CLIENT_UI_METHODS, diff --git a/src/main/runtime/rpc/methods/mobile-web-bundle-asset-reader.ts b/src/main/runtime/rpc/methods/mobile-web-bundle-asset-reader.ts new file mode 100644 index 00000000000..1cc87e02389 --- /dev/null +++ b/src/main/runtime/rpc/methods/mobile-web-bundle-asset-reader.ts @@ -0,0 +1,122 @@ +import { createHash } from 'node:crypto' +import { open } from 'node:fs/promises' +import { join } from 'node:path' +import type { MobileWebBundleAsset } from '../../../../shared/mobile-web-bundle/manifest-contract' + +// Why keyed by buildId as well as path: buildId is a content hash, so a dev rebuild that swaps the +// bundle under a running app can never reuse a verdict recorded against the previous bytes. +const verdicts = new Map>() + +/** Tests own the process, so they own the cache; nothing in the app may call this. */ +export function resetMobileWebBundleAssetVerdictsForTests(): void { + verdicts.clear() +} + +/** + * Whether the bytes on disk still hash to what the manifest promised, computed once per asset and + * then remembered. Concurrent first readers share one hash: the promise goes into the map before + * the first await, so four parallel chunk requests for the same asset read it once, not four times. + */ +export function verifyMobileWebBundleAsset( + root: string, + buildId: string, + asset: MobileWebBundleAsset +): Promise { + const key = `${buildId} ${asset.path}` + const cached = verdicts.get(key) + if (cached) { + return cached + } + const verdict = hashAsset(root, asset).then(undefined, (error: unknown) => { + // A read that failed is not evidence the bytes changed, so it is not remembered as a verdict. + verdicts.delete(key) + throw error + }) + verdicts.set(key, verdict) + return verdict +} + +async function hashAsset(root: string, asset: MobileWebBundleAsset): Promise { + const handle = await open(join(root, asset.path), 'r') + try { + const hash = createHash('sha256') + // Streamed rather than read whole: the contract ceiling is 10 MiB per asset, and this runs on + // the main process's event loop. + for await (const block of handle.createReadStream({ autoClose: false })) { + hash.update(block) + } + return hash.digest('hex') === asset.sha256 + } finally { + await handle.close() + } +} + +/** + * The bytes of one asset in the range starting at `offset`, clamped to the asset's manifest length. + * The window is always filled: a read that stops early only means the file really ended, which the + * caller answers as a changed asset instead of paging a client past a truncation. + * + * Measured through asar (Electron 43): `open` hands back a descriptor on a per-asset copy the asar + * layer materialises once under the OS temp dir and then reuses for the life of the process, so a + * positional read costs one pread and never re-inflates the archive. Nothing to cache here. + */ +export async function readMobileWebBundleAssetChunk( + root: string, + asset: MobileWebBundleAsset, + offset: number, + length: number +): Promise { + const wanted = Math.min(length, Math.max(0, asset.byteLength - offset)) + const buffer = Buffer.alloc(wanted) + if (wanted === 0) { + return buffer + } + // `asset.path` is a manifest member the caller matched exactly, never a client string, and the + // manifest schema already rejects absolute paths, backslashes, and traversal segments. + const handle = await open(join(root, asset.path), 'r') + try { + const filled = await fillMobileWebBundleReadWindow(handle, buffer, wanted, offset) + if (filled !== wanted) { + throw new Error( + `short read of ${asset.path}: ${String(filled)} of ${String(wanted)} bytes at ${String(offset)}` + ) + } + return buffer + } finally { + await handle.close() + } +} + +/** Just the member the window fill needs, so it can be driven by a stub, like the relay's + * `readFullStreamChunk` it mirrors. That one is not imported: it sits behind the relay + * dispatcher's module graph, which the runtime bundle has no business pulling in. */ +type PositionalReader = { + read( + buffer: Buffer, + offset: number, + length: number, + position: number + ): Promise<{ bytesRead: number }> +} + +/** + * Bytes actually placed in `buffer`, reading until the window is full. `read` may answer short of + * what it was asked for before EOF, so a single call is not evidence of anything; only a read that + * returns nothing means the file ended early. + */ +export async function fillMobileWebBundleReadWindow( + reader: PositionalReader, + buffer: Buffer, + wanted: number, + offset: number +): Promise { + let filled = 0 + while (filled < wanted) { + const { bytesRead } = await reader.read(buffer, filled, wanted - filled, offset + filled) + if (bytesRead === 0) { + break + } + filled += bytesRead + } + return filled +} diff --git a/src/main/runtime/rpc/methods/mobile-web-bundle-read-admission.ts b/src/main/runtime/rpc/methods/mobile-web-bundle-read-admission.ts new file mode 100644 index 00000000000..505ca2b9391 --- /dev/null +++ b/src/main/runtime/rpc/methods/mobile-web-bundle-read-admission.ts @@ -0,0 +1,45 @@ +import type { RpcContext } from '../core' + +/** Enough for a client to keep the pipe full without letting one phone own the disk. */ +export const MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS = 4 + +const activeReads = new Map() + +/** Tests own the process, so they own the counters; nothing in the app may call this. */ +export function resetMobileWebBundleReadAdmissionForTests(): void { + activeReads.clear() +} + +/** Buckets currently holding at least one read. Exported so a test can prove the map does not + * retain a device token per socket; nothing in the app may call this. */ +export function mobileWebBundleReadBucketCountForTests(): number { + return activeReads.size +} + +/** + * The bucket a chunk read is charged to. `connectionId` is set only for E2EE mobile sockets, so + * keying on it alone would leave a plain-WebSocket phone in one shared unbounded bucket; the device + * token still names one client. An in-process caller has neither and is not the caller this bounds. + */ +export function mobileWebBundleReadBucket(ctx: RpcContext): string { + return ctx.connectionId ?? ctx.clientId ?? 'local' +} + +/** A slot in the bucket's budget, or null when it is already full. Release exactly once. */ +export function acquireMobileWebBundleReadSlot(bucket: string): (() => void) | null { + const active = activeReads.get(bucket) ?? 0 + if (active >= MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS) { + return null + } + activeReads.set(bucket, active + 1) + return () => { + const remaining = (activeReads.get(bucket) ?? 1) - 1 + // Dropping the key at zero is what keeps this from retaining one entry per socket forever — + // and off the E2EE channel the key is the device's pairing token. + if (remaining > 0) { + activeReads.set(bucket, remaining) + } else { + activeReads.delete(bucket) + } + } +} diff --git a/src/main/runtime/rpc/methods/mobile-web-bundle-read-concurrency.test.ts b/src/main/runtime/rpc/methods/mobile-web-bundle-read-concurrency.test.ts new file mode 100644 index 00000000000..9d628182083 --- /dev/null +++ b/src/main/runtime/rpc/methods/mobile-web-bundle-read-concurrency.test.ts @@ -0,0 +1,253 @@ +/** + * The behaviours that only exist while a read is genuinely in flight or genuinely failing: the + * per-connection cap, an abort that arrives mid-read, and a verify whose open throws. All three go + * through a gate on `open`, so none of them depends on a race between an event loop and a stopwatch. + */ +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import type * as FsPromises from 'node:fs/promises' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { RpcResponse } from '../core' +import type { RpcDispatcher } from '../dispatcher' + +/** A latch on `open`, so a read can be held mid-flight without racing a stopwatch. */ +type OpenGate = { + blocker: Promise | null + unlatch: (() => void) | null + opens: number + failures: number + hold(): void + release(): void + failNextOpen(): void + reset(): void +} + +const { gate } = vi.hoisted(() => { + const gate: OpenGate = { + blocker: null, + unlatch: null, + opens: 0, + failures: 0, + hold() { + gate.blocker = new Promise((resolve) => { + gate.unlatch = resolve + }) + }, + release() { + gate.unlatch?.() + gate.blocker = null + gate.unlatch = null + }, + failNextOpen() { + gate.failures++ + }, + reset() { + gate.release() + gate.opens = 0 + gate.failures = 0 + } + } + return { gate } +}) + +vi.mock('node:fs/promises', async () => { + const actual = await vi.importActual('node:fs/promises') + return { + ...actual, + default: actual, + open: async (...args: Parameters) => { + gate.opens++ + if (gate.blocker) { + await gate.blocker + } + if (gate.failures > 0) { + gate.failures-- + throw new Error('EIO: i/o error, open') + } + return actual.open(...args) + } + } +}) + +import { resetBundledMobileWebBundleCacheForTests } from '../../bundled-mobile-web-bundle' +import { resetMobileWebBundleAssetVerdictsForTests } from './mobile-web-bundle-asset-reader' +import { + MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS, + resetMobileWebBundleReadAdmissionForTests +} from './mobile-web-bundle-read-admission' +import { + installMobileWebBundleAppPath, + mobileWebBundleDispatcher, + writeSyntheticMobileWebBundle, + type SyntheticMobileWebBundle +} from './mobile-web-bundle.test-fixture' + +let scratch: string +let bundle: SyntheticMobileWebBundle +let dispatcher: RpcDispatcher + +type DispatchOptions = { connectionId?: string; signal?: AbortSignal } + +function chunk(offset: number, options?: DispatchOptions): Promise { + return dispatcher.dispatch( + { + id: `chunk-${String(offset)}`, + authToken: 'tok', + method: 'mobileWeb.bundle.chunk', + params: { buildId: bundle.buildId, path: 'index.html', offset } + }, + options + ) +} + +function errorMessage(response: RpcResponse): string | undefined { + return response.ok ? undefined : response.error.message +} + +/** Lets every already-scheduled continuation run, without advancing any clock. */ +async function settleMicrotasks(): Promise { + for (let turn = 0; turn < 20; turn++) { + await Promise.resolve() + } +} + +beforeEach(() => { + scratch = mkdtempSync(join(tmpdir(), 'orca-mobile-web-reads-')) + installMobileWebBundleAppPath(scratch) + bundle = writeSyntheticMobileWebBundle(join(scratch, 'out', 'mobile-web'), 7) + gate.reset() + resetBundledMobileWebBundleCacheForTests() + resetMobileWebBundleAssetVerdictsForTests() + resetMobileWebBundleReadAdmissionForTests() + vi.spyOn(console, 'warn').mockImplementation(() => {}) + dispatcher = mobileWebBundleDispatcher() +}) + +afterEach(() => { + gate.reset() + rmSync(scratch, { recursive: true, force: true }) + vi.restoreAllMocks() +}) + +describe('chunk reads in flight on one connection', () => { + // Pinned as a literal because every other case here is written in terms of the constant, so the + // budget itself would otherwise move silently with it. + it('budgets four', () => { + expect(MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS).toBe(4) + }) + + it('admits four and refuses the fifth, then admits it once one finishes', async () => { + gate.hold() + const inFlight = Array.from({ length: MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS }, () => + chunk(0, { connectionId: 'conn-1' }) + ) + await settleMicrotasks() + + const overflow = await chunk(0, { connectionId: 'conn-1' }) + expect(errorMessage(overflow)).toBe('mobile_web_bundle_read_limited') + + gate.release() + const admitted = await Promise.all(inFlight) + expect(admitted.every((response) => response.ok)).toBe(true) + + const afterwards = await chunk(0, { connectionId: 'conn-1' }) + expect(afterwards.ok).toBe(true) + }) + + it('does not let one connection at its cap cost another connection a read', async () => { + gate.hold() + const saturating = Array.from({ length: MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS }, () => + chunk(0, { connectionId: 'conn-1' }) + ) + await settleMicrotasks() + + const neighbour = chunk(0, { connectionId: 'conn-2' }) + await settleMicrotasks() + gate.release() + + expect((await neighbour).ok).toBe(true) + expect((await Promise.all(saturating)).every((response) => response.ok)).toBe(true) + }) + + it('hashes an asset once even when four first readers arrive together', async () => { + gate.hold() + const together = Array.from({ length: MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS }, () => + chunk(0, { connectionId: 'conn-1' }) + ) + await settleMicrotasks() + + // One verification open for the four of them; the rest are the four chunk reads. + const opensBeforeRelease = gate.opens + gate.release() + await Promise.all(together) + + expect(opensBeforeRelease).toBe(1) + expect(gate.opens).toBe(1 + MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS) + }) +}) + +describe('a client that disconnects while its chunk is being read', () => { + it('stops before the chunk read, and answers nothing it had already produced', async () => { + const controller = new AbortController() + gate.hold() + const pending = chunk(0, { connectionId: 'conn-3', signal: controller.signal }) + await settleMicrotasks() + expect(gate.opens).toBe(1) + + controller.abort() + gate.release() + const response = await pending + + expect(response.ok).toBe(false) + expect(errorMessage(response)).toBe('client_disconnected') + // The verification open happened before the abort; the chunk read never did. + expect(gate.opens).toBe(1) + }) + + // Honouring `signal` exists so a client that is gone stops costing file reads. Verification + // streams the whole asset, up to the contract's 10 MiB ceiling, so the check that matters is the + // one before it: not a single open. + it('does not hash the asset at all when the signal was already aborted', async () => { + const controller = new AbortController() + controller.abort() + + const response = await chunk(0, { connectionId: 'conn-6', signal: controller.signal }) + + expect(errorMessage(response)).toBe('client_disconnected') + expect(gate.opens).toBe(0) + }) + + it('releases the slot it was holding, so the connection is not permanently capped', async () => { + const aborted = Array.from({ length: MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS }, () => { + const controller = new AbortController() + return { + controller, + response: chunk(0, { connectionId: 'conn-4', signal: controller.signal }) + } + }) + gate.hold() + await settleMicrotasks() + for (const { controller } of aborted) { + controller.abort() + } + gate.release() + await Promise.all(aborted.map(({ response }) => response)) + + expect((await chunk(0, { connectionId: 'conn-4' })).ok).toBe(true) + }) +}) + +describe('a verify whose read of the asset fails', () => { + // The verdict cache is never invalidated, so remembering a transient EIO as "these bytes are + // wrong" would poison the asset until the desktop restarts. + it('is not remembered as a verdict, so the next read still verifies', async () => { + gate.failNextOpen() + + const failed = await chunk(0, { connectionId: 'conn-5' }) + const retried = await chunk(0, { connectionId: 'conn-5' }) + + expect(errorMessage(failed)).toBe('mobile_web_bundle_asset_changed') + expect(retried.ok).toBe(true) + }) +}) diff --git a/src/main/runtime/rpc/methods/mobile-web-bundle.test-fixture.ts b/src/main/runtime/rpc/methods/mobile-web-bundle.test-fixture.ts new file mode 100644 index 00000000000..f955bfabd76 --- /dev/null +++ b/src/main/runtime/rpc/methods/mobile-web-bundle.test-fixture.ts @@ -0,0 +1,106 @@ +import { createHash } from 'node:crypto' +import { mkdirSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import { installFakeAppEnvironment } from '../../../../../config/scripts/vitest-host-ports-setup' +import type { OrcaRuntimeService } from '../../orca-runtime' +import { RpcDispatcher } from '../dispatcher' +import { MOBILE_WEB_BUNDLE_METHODS } from './mobile-web-bundle' +import { MOBILE_WEB_BUNDLE_CHUNK_BYTES } from '../../../../shared/mobile-web-bundle/bundle-rpc-contract' +import { + computeMobileWebBundleId, + type MobileWebBundleAsset +} from '../../../../shared/mobile-web-bundle/manifest-contract' + +export const sha256Hex = (bytes: Buffer): string => createHash('sha256').update(bytes).digest('hex') + +/** Deterministic, varied bytes, so a read at the wrong offset cannot accidentally look right. */ +export function mobileWebBundleFiller(byteLength: number, seed: number): Buffer { + const bytes = Buffer.alloc(byteLength) + for (let index = 0; index < byteLength; index++) { + bytes[index] = (index * 31 + seed * 17) % 256 + } + return bytes +} + +type SyntheticAsset = { path: string; bytes: Buffer; contentType: string } + +/** + * A bundle the real builder cannot produce today: its largest asset spans three chunks, where every + * asset the Phase A bootstrap emits is under one. Multi-chunk paging has to be exercised rather than + * assumed, and CI unit jobs never build out/mobile-web, so the fixture is synthetic on purpose. + */ +function syntheticAssets(seed: number): SyntheticAsset[] { + const script = mobileWebBundleFiller(MOBILE_WEB_BUNDLE_CHUNK_BYTES * 2 + 1024, seed) + const stylesheet = mobileWebBundleFiller(MOBILE_WEB_BUNDLE_CHUNK_BYTES, seed + 1) + const mark = Buffer.alloc(0) + return [ + { + path: 'index.html', + bytes: mobileWebBundleFiller(640, seed + 2), + contentType: 'text/html; charset=utf-8' + }, + { + path: `assets/${sha256Hex(script)}.js`, + bytes: script, + contentType: 'text/javascript; charset=utf-8' + }, + { path: `assets/${sha256Hex(stylesheet)}.css`, bytes: stylesheet, contentType: 'text/css' }, + { path: `assets/${sha256Hex(mark)}.png`, bytes: mark, contentType: 'image/png' } + ] +} + +export type SyntheticMobileWebBundle = { + root: string + buildId: string + assets: MobileWebBundleAsset[] +} + +export function writeSyntheticMobileWebBundle( + root: string, + seed: number +): SyntheticMobileWebBundle { + mkdirSync(join(root, 'assets'), { recursive: true }) + const written = syntheticAssets(seed) + for (const asset of written) { + writeFileSync(join(root, asset.path), asset.bytes) + } + const assets = written + .map((asset) => ({ + path: asset.path, + sha256: sha256Hex(asset.bytes), + byteLength: asset.bytes.byteLength, + contentType: asset.contentType + })) + .sort((left, right) => (left.path < right.path ? -1 : 1)) + const buildId = computeMobileWebBundleId(assets) + writeFileSync( + join(root, 'manifest.json'), + JSON.stringify({ + schemaVersion: 1, + buildId, + desktopVersion: '1.4.200', + minCompatibleRuntimeProtocolVersion: 2, + runtimeProtocolVersion: 2, + entrypoint: 'index.html', + totalBytes: assets.reduce((total, asset) => total + asset.byteLength, 0), + assets + }), + 'utf8' + ) + return { root, buildId, assets } +} + +/** A dispatcher carrying only these methods. Nothing here reaches the runtime service: the bundle is + * read off the install, so the dispatcher's one call into it is the envelope's runtime id. */ +export function mobileWebBundleDispatcher(): RpcDispatcher { + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: neither mobileWeb.bundle method takes a runtime argument, so getRuntimeId (read once, to stamp the envelope) is the only member this dispatcher can reach. + const runtime = { getRuntimeId: () => 'test-runtime' } as unknown as OrcaRuntimeService + return new RpcDispatcher({ runtime, methods: MOBILE_WEB_BUNDLE_METHODS }) +} + +/** The install root the resolver probes. Installed through the port, not an electron mock: the + * resolver is reachable from the runtime's import graph and so must never import electron. The + * shared setup reinstalls a default environment before every test, so nothing here needs undoing. */ +export function installMobileWebBundleAppPath(appPath: string): void { + installFakeAppEnvironment({ getAppPath: () => appPath, getPath: () => appPath }) +} diff --git a/src/main/runtime/rpc/methods/mobile-web-bundle.test.ts b/src/main/runtime/rpc/methods/mobile-web-bundle.test.ts new file mode 100644 index 00000000000..b04c7f18129 --- /dev/null +++ b/src/main/runtime/rpc/methods/mobile-web-bundle.test.ts @@ -0,0 +1,552 @@ +import { mkdirSync, mkdtempSync, rmSync, truncateSync, unlinkSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { + MOBILE_WEB_BUNDLE_CHUNK_BYTES, + MOBILE_WEB_BUNDLE_CHUNK_METHOD, + MOBILE_WEB_BUNDLE_MANIFEST_METHOD, + MobileWebBundleChunkResultSchema, + MobileWebBundleManifestResultSchema +} from '../../../../shared/mobile-web-bundle/bundle-rpc-contract' +import { MOBILE_RPC_METHOD_ALLOWLIST } from '../../runtime-rpc/runtime-rpc-mobile-method-allowlist' +import type { RpcRequest, RpcResponse } from '../core' +import type { RpcDispatcher } from '../dispatcher' + +import { + getBundledMobileWebBundleRoot, + resetBundledMobileWebBundleCacheForTests +} from '../../bundled-mobile-web-bundle' +import { + fillMobileWebBundleReadWindow, + resetMobileWebBundleAssetVerdictsForTests +} from './mobile-web-bundle-asset-reader' +import { + acquireMobileWebBundleReadSlot, + MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS, + mobileWebBundleReadBucketCountForTests, + resetMobileWebBundleReadAdmissionForTests +} from './mobile-web-bundle-read-admission' +import { + installMobileWebBundleAppPath, + mobileWebBundleDispatcher, + mobileWebBundleFiller, + sha256Hex, + writeSyntheticMobileWebBundle, + type SyntheticMobileWebBundle +} from './mobile-web-bundle.test-fixture' + +let scratch: string +let dispatcher: RpcDispatcher + +function request(method: string, params?: unknown): RpcRequest { + return { id: `req-${method}`, authToken: 'tok', method, params } +} + +type DispatchOptions = { connectionId?: string; clientId?: string; signal?: AbortSignal } + +async function call(method: string, params?: unknown, options?: DispatchOptions) { + return dispatcher.dispatch(request(method, params), options) +} + +function errorMessage(response: RpcResponse): string | undefined { + return response.ok ? undefined : response.error.message +} + +async function chunk(params: unknown, options?: DispatchOptions) { + return call('mobileWeb.bundle.chunk', params, options) +} + +/** Pages one asset to the end the way a client must: never assuming a size it did not read. */ +async function download(buildId: string, path: string): Promise<{ bytes: Buffer; calls: number }> { + const pieces: Buffer[] = [] + let offset = 0 + let calls = 0 + for (;;) { + const response = await chunk({ buildId, path, offset }) + calls++ + if (!response.ok) { + throw new Error(`chunk at ${String(offset)} failed: ${response.error.message}`) + } + const body = MobileWebBundleChunkResultSchema.parse(response.result) + expect(body.buildId).toBe(buildId) + expect(body.path).toBe(path) + expect(body.offset).toBe(offset) + pieces.push(Buffer.from(body.dataBase64, 'base64')) + if (body.eof) { + expect(offset + pieces.at(-1)!.byteLength).toBe(body.assetByteLength) + break + } + offset += MOBILE_WEB_BUNDLE_CHUNK_BYTES + } + return { bytes: Buffer.concat(pieces), calls } +} + +beforeEach(() => { + scratch = mkdtempSync(join(tmpdir(), 'orca-mobile-web-bundle-')) + installMobileWebBundleAppPath(scratch) + resetBundledMobileWebBundleCacheForTests() + resetMobileWebBundleAssetVerdictsForTests() + resetMobileWebBundleReadAdmissionForTests() + vi.spyOn(console, 'warn').mockImplementation(() => {}) + dispatcher = mobileWebBundleDispatcher() +}) + +afterEach(() => { + rmSync(scratch, { recursive: true, force: true }) + vi.restoreAllMocks() +}) + +describe('an install that carries a mobile web bundle', () => { + let bundle: SyntheticMobileWebBundle + + beforeEach(() => { + bundle = writeSyntheticMobileWebBundle(join(scratch, 'out', 'mobile-web'), 1) + }) + + it('answers the manifest with the chunk size it will actually serve', async () => { + const response = await call('mobileWeb.bundle.manifest') + + expect(response.ok).toBe(true) + const body = MobileWebBundleManifestResultSchema.parse( + response.ok ? response.result : undefined + ) + expect(body.chunkBytes).toBe(MOBILE_WEB_BUNDLE_CHUNK_BYTES) + expect(body.manifest.buildId).toBe(bundle.buildId) + expect(body.manifest.assets).toEqual(bundle.assets) + }) + + // Read once per process: without the cache every chunk request re-parses the manifest, and the + // schema's refinement recomputes the buildId with a pure-JS sha256 on the event loop. + it('answers from the manifest it already read, without going back to disk', async () => { + const first = await call('mobileWeb.bundle.manifest') + writeFileSync(join(bundle.root, 'manifest.json'), 'not json', 'utf8') + + const second = await call('mobileWeb.bundle.manifest') + + expect(errorMessage(second)).toBeUndefined() + expect(MobileWebBundleManifestResultSchema.parse(second.ok && second.result).manifest).toEqual( + MobileWebBundleManifestResultSchema.parse(first.ok && first.result).manifest + ) + }) + + it('pages every asset back byte for byte, and each reassembly matches its manifest hash', async () => { + for (const asset of bundle.assets) { + const { bytes, calls } = await download(bundle.buildId, asset.path) + + expect(bytes.byteLength).toBe(asset.byteLength) + expect(sha256Hex(bytes)).toBe(asset.sha256) + expect(calls).toBe(Math.max(1, Math.ceil(asset.byteLength / MOBILE_WEB_BUNDLE_CHUNK_BYTES))) + } + }) + + it('reports eof only on the last chunk of a multi-chunk asset', async () => { + const script = bundle.assets.find((asset) => asset.path.endsWith('.js'))! + expect(script.byteLength).toBeGreaterThan(MOBILE_WEB_BUNDLE_CHUNK_BYTES * 2) + + const eofs: boolean[] = [] + for (let offset = 0; offset < script.byteLength; offset += MOBILE_WEB_BUNDLE_CHUNK_BYTES) { + const response = await chunk({ buildId: bundle.buildId, path: script.path, offset }) + expect(response.ok).toBe(true) + eofs.push(MobileWebBundleChunkResultSchema.parse(response.ok && response.result).eof) + } + + expect(eofs).toEqual([false, false, true]) + }) + + // An asset whose length is an exact multiple of the chunk size must still end somewhere, and the + // only offset a client could try next is one the host rejects. + it('ends an exactly-one-chunk asset on its first chunk', async () => { + const stylesheet = bundle.assets.find((asset) => asset.path.endsWith('.css'))! + expect(stylesheet.byteLength).toBe(MOBILE_WEB_BUNDLE_CHUNK_BYTES) + + const first = await chunk({ buildId: bundle.buildId, path: stylesheet.path, offset: 0 }) + const past = await chunk({ + buildId: bundle.buildId, + path: stylesheet.path, + offset: MOBILE_WEB_BUNDLE_CHUNK_BYTES + }) + + expect(MobileWebBundleChunkResultSchema.parse(first.ok && first.result).eof).toBe(true) + expect(errorMessage(past)).toBe('mobile_web_bundle_offset_invalid') + }) + + // Offset 0 is in range for every asset, including an empty one, so a client never has to special + // case a zero-byte member it cannot ask about. + it('serves a zero-byte asset as one empty chunk at eof', async () => { + const mark = bundle.assets.find((asset) => asset.byteLength === 0)! + + const response = await chunk({ buildId: bundle.buildId, path: mark.path, offset: 0 }) + + const body = MobileWebBundleChunkResultSchema.parse(response.ok && response.result) + expect(body).toMatchObject({ dataBase64: '', eof: true, assetByteLength: 0 }) + }) + + it('describes the whole asset on every chunk, not the chunk', async () => { + const script = bundle.assets.find((asset) => asset.path.endsWith('.js'))! + + const middle = await chunk({ + buildId: bundle.buildId, + path: script.path, + offset: MOBILE_WEB_BUNDLE_CHUNK_BYTES + }) + + const body = MobileWebBundleChunkResultSchema.parse(middle.ok && middle.result) + expect(body.assetByteLength).toBe(script.byteLength) + expect(body.sha256).toBe(script.sha256) + expect(Buffer.from(body.dataBase64, 'base64').byteLength).toBe(MOBILE_WEB_BUNDLE_CHUNK_BYTES) + }) + + it('refuses a path that is not a manifest member', async () => { + const attempts = [ + 'assets/does-not-exist.js', + 'manifest.json', + 'index.htm', + 'assets', + 'INDEX.HTML' + ] + + for (const path of attempts) { + const response = await chunk({ buildId: bundle.buildId, path, offset: 0 }) + expect(errorMessage(response)).toBe('mobile_web_bundle_asset_unknown') + } + }) + + it('rejects a traversal path at the params schema, before any lookup', async () => { + const response = await chunk({ buildId: bundle.buildId, path: '../../etc/passwd', offset: 0 }) + + expect(response.ok).toBe(false) + expect(errorMessage(response)).not.toBe('mobile_web_bundle_asset_unknown') + }) + + it('refuses an offset that does not address a chunk boundary', async () => { + const script = bundle.assets.find((asset) => asset.path.endsWith('.js'))! + + for (const offset of [1, 1024, MOBILE_WEB_BUNDLE_CHUNK_BYTES - 1, 49_153]) { + const response = await chunk({ buildId: bundle.buildId, path: script.path, offset }) + expect(errorMessage(response)).toBe('mobile_web_bundle_offset_invalid') + } + }) + + it('refuses an aligned offset that starts past the end of the asset', async () => { + const index = bundle.assets.find((asset) => asset.path === 'index.html')! + expect(index.byteLength).toBeLessThan(MOBILE_WEB_BUNDLE_CHUNK_BYTES) + + const response = await chunk({ + buildId: bundle.buildId, + path: index.path, + offset: MOBILE_WEB_BUNDLE_CHUNK_BYTES + }) + + expect(errorMessage(response)).toBe('mobile_web_bundle_offset_invalid') + }) + + it('refuses a buildId that is not the one it is serving', async () => { + const response = await chunk({ + buildId: '0'.repeat(64), + path: 'index.html', + offset: 0 + }) + + expect(errorMessage(response)).toBe('mobile_web_bundle_build_changed') + }) + + // The auto-update case: the desktop replaced the bundle between the client's manifest call and + // its next chunk. The client must be told to restart from the manifest, not that its path is + // gone, so this is checked before the asset lookup. + it('refuses the old buildId after the install swaps bundles mid-download', async () => { + const script = bundle.assets.find((asset) => asset.path.endsWith('.js'))! + expect((await chunk({ buildId: bundle.buildId, path: script.path, offset: 0 })).ok).toBe(true) + + rmSync(join(scratch, 'out', 'mobile-web'), { recursive: true, force: true }) + const replacement = writeSyntheticMobileWebBundle(join(scratch, 'out', 'mobile-web'), 2) + resetBundledMobileWebBundleCacheForTests() + expect(replacement.buildId).not.toBe(bundle.buildId) + + const stale = await chunk({ buildId: bundle.buildId, path: script.path, offset: 0 }) + + expect(errorMessage(stale)).toBe('mobile_web_bundle_build_changed') + }) + + // index.html is the one path a rebuild keeps, so a verdict keyed by path alone would carry build + // A's `false` onto build B's honest file and refuse it for the life of the process. + it('does not carry a failed verdict from one build onto the next build of the same path', async () => { + writeFileSync(join(bundle.root, 'index.html'), mobileWebBundleFiller(640, 99)) + expect( + errorMessage(await chunk({ buildId: bundle.buildId, path: 'index.html', offset: 0 })) + ).toBe('mobile_web_bundle_asset_changed') + + rmSync(join(scratch, 'out', 'mobile-web'), { recursive: true, force: true }) + const replacement = writeSyntheticMobileWebBundle(join(scratch, 'out', 'mobile-web'), 8) + resetBundledMobileWebBundleCacheForTests() + + const response = await chunk({ buildId: replacement.buildId, path: 'index.html', offset: 0 }) + + expect(errorMessage(response)).toBeUndefined() + }) + + it('refuses an asset whose bytes on disk no longer hash to the manifest', async () => { + const script = bundle.assets.find((asset) => asset.path.endsWith('.js'))! + writeFileSync(join(bundle.root, script.path), mobileWebBundleFiller(script.byteLength, 99)) + + const response = await chunk({ buildId: bundle.buildId, path: script.path, offset: 0 }) + + expect(errorMessage(response)).toBe('mobile_web_bundle_asset_changed') + }) + + // A dev rebuild under a live runtime, or a permissions change, reaches the filesystem after the + // verdict is already cached. The client must still land inside the six codes, and the host's + // absolute install path must not ride out on the reply. + it('answers a changed asset, not the filesystem error, when the asset is gone after its verdict', async () => { + const script = bundle.assets.find((asset) => asset.path.endsWith('.js'))! + expect((await chunk({ buildId: bundle.buildId, path: script.path, offset: 0 })).ok).toBe(true) + unlinkSync(join(bundle.root, script.path)) + + const response = await chunk({ + buildId: bundle.buildId, + path: script.path, + offset: MOBILE_WEB_BUNDLE_CHUNK_BYTES + }) + + expect(errorMessage(response)).toBe('mobile_web_bundle_asset_changed') + expect(console.warn).toHaveBeenCalled() + }) + + // The only way a positional read on a regular file comes back short: the file was truncated after + // its verdict was cached. Answering the short chunk would page the client past the truncation. + it('answers a changed asset when the file is shorter than the manifest promised', async () => { + const script = bundle.assets.find((asset) => asset.path.endsWith('.js'))! + expect((await chunk({ buildId: bundle.buildId, path: script.path, offset: 0 })).ok).toBe(true) + truncateSync(join(bundle.root, script.path), 100) + + const response = await chunk({ buildId: bundle.buildId, path: script.path, offset: 0 }) + + expect(errorMessage(response)).toBe('mobile_web_bundle_asset_changed') + }) + + // Deliberate: a packaged bundle is immutable for the life of the install, so the verdict is worth + // one hash per asset rather than one per 48 KiB. Restoring the bytes without restarting is a dev + // scenario, and it stays refused until the process does. + it('remembers the verdict, so one hash per asset covers every later chunk', async () => { + const script = bundle.assets.find((asset) => asset.path.endsWith('.js'))! + const corrupted = mobileWebBundleFiller(script.byteLength, 99) + writeFileSync(join(bundle.root, script.path), corrupted) + expect( + errorMessage(await chunk({ buildId: bundle.buildId, path: script.path, offset: 0 })) + ).toBe('mobile_web_bundle_asset_changed') + + writeFileSync(join(bundle.root, script.path), mobileWebBundleFiller(script.byteLength, 1)) + + expect( + errorMessage(await chunk({ buildId: bundle.buildId, path: script.path, offset: 0 })) + ).toBe('mobile_web_bundle_asset_changed') + resetMobileWebBundleAssetVerdictsForTests() + expect((await chunk({ buildId: bundle.buildId, path: script.path, offset: 0 })).ok).toBe(true) + }) + + it('charges reads to the connection, and refuses one past the cap', async () => { + const held = Array.from({ length: MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS }, () => + acquireMobileWebBundleReadSlot('conn-a') + ) + expect(held.every((release) => release !== null)).toBe(true) + + const refused = await chunk( + { buildId: bundle.buildId, path: 'index.html', offset: 0 }, + { + connectionId: 'conn-a' + } + ) + const other = await chunk( + { buildId: bundle.buildId, path: 'index.html', offset: 0 }, + { + connectionId: 'conn-b' + } + ) + + expect(errorMessage(refused)).toBe('mobile_web_bundle_read_limited') + // One phone at its cap must not cost another phone a thing. + expect(other.ok).toBe(true) + + held[0]!() + expect( + ( + await chunk( + { buildId: bundle.buildId, path: 'index.html', offset: 0 }, + { + connectionId: 'conn-a' + } + ) + ).ok + ).toBe(true) + }) + + // Off the E2EE channel the bucket key is the device's pairing token, so a map that never drops a + // key retains one credential per socket, and reconnect churn is normal on mobile. + it('keeps no bucket for a connection that finished its reads', () => { + for (let socket = 0; socket < 50; socket++) { + const release = acquireMobileWebBundleReadSlot(`device-token-${String(socket)}`) + expect(release).not.toBeNull() + release?.() + } + + expect(mobileWebBundleReadBucketCountForTests()).toBe(0) + }) + + // connectionId is set only for E2EE mobile sockets, so the device token is what keeps a + // plain-WebSocket phone from sharing one unbounded bucket with every other caller. + it('falls back to the device token when the connection has no id', async () => { + const held = Array.from({ length: MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS }, () => + acquireMobileWebBundleReadSlot('device-token-1') + ) + expect(held.every((release) => release !== null)).toBe(true) + + const refused = await chunk( + { buildId: bundle.buildId, path: 'index.html', offset: 0 }, + { + clientId: 'device-token-1' + } + ) + + expect(errorMessage(refused)).toBe('mobile_web_bundle_read_limited') + }) + + it('stops before reading anything for a client that already disconnected', async () => { + const controller = new AbortController() + controller.abort() + + const response = await chunk( + { buildId: bundle.buildId, path: 'index.html', offset: 0 }, + { + signal: controller.signal + } + ) + + expect(response.ok).toBe(false) + expect(errorMessage(response)).toBe('client_disconnected') + }) + + it('gives the slot back after an abort, so the cap does not leak', async () => { + const controller = new AbortController() + controller.abort() + await chunk( + { buildId: bundle.buildId, path: 'index.html', offset: 0 }, + { + connectionId: 'conn-c', + signal: controller.signal + } + ) + + const held = Array.from({ length: MAX_CONCURRENT_MOBILE_WEB_BUNDLE_READS }, () => + acquireMobileWebBundleReadSlot('conn-c') + ) + + expect(held.every((release) => release !== null)).toBe(true) + }) +}) + +describe('where the resolver probes', () => { + it('finds out/mobile-web under the install root', () => { + const bundle = writeSyntheticMobileWebBundle(join(scratch, 'out', 'mobile-web'), 5) + + expect(getBundledMobileWebBundleRoot()).toBe(bundle.root) + }) + + it('answers undefined when neither layout holds a manifest', () => { + expect(getBundledMobileWebBundleRoot()).toBeUndefined() + }) + + // Unpacked electron-vite entrypoints set appPath to out/main, next to the bundle. + it('finds the bundle beside an out/main app path', async () => { + const bundle = writeSyntheticMobileWebBundle(join(scratch, 'out', 'mobile-web'), 3) + installMobileWebBundleAppPath(join(scratch, 'out', 'main')) + resetBundledMobileWebBundleCacheForTests() + + const response = await call('mobileWeb.bundle.manifest') + + expect( + MobileWebBundleManifestResultSchema.parse(response.ok && response.result).manifest.buildId + ).toBe(bundle.buildId) + }) +}) + +describe('an install with no mobile web bundle', () => { + it('reports both methods unavailable rather than failing some other way', async () => { + const manifest = await call('mobileWeb.bundle.manifest') + const body = await chunk({ buildId: '0'.repeat(64), path: 'index.html', offset: 0 }) + + expect(errorMessage(manifest)).toBe('mobile_web_bundle_unavailable') + expect(errorMessage(body)).toBe('mobile_web_bundle_unavailable') + }) + + it('reads a manifest that does not match the contract as no bundle at all', async () => { + const root = join(scratch, 'out', 'mobile-web') + writeSyntheticMobileWebBundle(root, 4) + writeFileSync(join(root, 'manifest.json'), '{"schemaVersion":2}', 'utf8') + resetBundledMobileWebBundleCacheForTests() + + const response = await call('mobileWeb.bundle.manifest') + + expect(errorMessage(response)).toBe('mobile_web_bundle_unavailable') + expect(console.warn).toHaveBeenCalled() + }) + + it('reads an unparseable manifest as no bundle at all', async () => { + const root = join(scratch, 'out', 'mobile-web') + mkdirSync(root, { recursive: true }) + writeFileSync(join(root, 'manifest.json'), 'not json', 'utf8') + resetBundledMobileWebBundleCacheForTests() + + expect(errorMessage(await call('mobileWeb.bundle.manifest'))).toBe( + 'mobile_web_bundle_unavailable' + ) + }) +}) + +// Registration in ALL_RPC_METHODS is pinned by the generated params catalog; authorization is not, +// and the mobile scanner only checks used ⊆ allowlist, so no mobile caller exists to miss these +// until A5 ships one. +describe('mobile authorization', () => { + it('lets a paired phone call both bundle methods', () => { + expect(MOBILE_RPC_METHOD_ALLOWLIST.has(MOBILE_WEB_BUNDLE_MANIFEST_METHOD)).toBe(true) + expect(MOBILE_RPC_METHOD_ALLOWLIST.has(MOBILE_WEB_BUNDLE_CHUNK_METHOD)).toBe(true) + }) +}) + +// fs.read may answer short of the window before EOF, so one call proves nothing; every other +// positional reader in the repo fills the window first, and a client must never be handed a short +// chunk because the kernel felt like splitting one. +describe('filling a read window', () => { + const source = mobileWebBundleFiller(64, 3) + + /** Answers `pieces[n]` bytes to the nth read, so a split window can be driven exactly. */ + function reader(pieces: number[]) { + const calls: number[] = [] + let piece = 0 + const read = async (buffer: Buffer, into: number, length: number, position: number) => { + calls.push(length) + const bytesRead = Math.min(pieces[piece++] ?? 0, length) + source.copy(buffer, into, position, position + bytesRead) + return { bytesRead } + } + return { calls, read } + } + + it('reads again when a read answers short of the window', async () => { + const buffer = Buffer.alloc(64) + const stub = reader([24, 40]) + + const filled = await fillMobileWebBundleReadWindow(stub, buffer, 64, 0) + + expect(filled).toBe(64) + expect(stub.calls).toEqual([64, 40]) + expect(buffer.equals(source)).toBe(true) + }) + + it('stops at the read that returns nothing, which is the truncation the caller reports', async () => { + const stub = reader([24, 0]) + + const filled = await fillMobileWebBundleReadWindow(stub, Buffer.alloc(64), 64, 0) + + expect(filled).toBe(24) + }) +}) diff --git a/src/main/runtime/rpc/methods/mobile-web-bundle.ts b/src/main/runtime/rpc/methods/mobile-web-bundle.ts new file mode 100644 index 00000000000..780da513287 --- /dev/null +++ b/src/main/runtime/rpc/methods/mobile-web-bundle.ts @@ -0,0 +1,145 @@ +/** + * Serves this install's mobile web bundle to the paired client over the already-authenticated RPC + * connection: one call for the manifest, then one call per 48 KiB chunk of each asset. + * + * No SSH or relay proxying, ever. The bundle is an artifact of the desktop the phone paired with, + * not something a remote execution host owns, so a runtime answers only out of its own install and + * never forwards these methods to another host. + * + * `asContractError` is a total catch over the verify-and-read block: every host-side failure in + * there, whatever its cause, reaches the client as `mobile_web_bundle_asset_changed`. + */ +import { + MOBILE_WEB_BUNDLE_CHUNK_BYTES, + MOBILE_WEB_BUNDLE_CHUNK_METHOD, + MOBILE_WEB_BUNDLE_MANIFEST_METHOD, + MobileWebBundleChunkParamsSchema, + type MobileWebBundleChunkResult, + type MobileWebBundleErrorCode, + type MobileWebBundleManifestResult +} from '../../../../shared/mobile-web-bundle/bundle-rpc-contract' +import type { MobileWebBundleAsset } from '../../../../shared/mobile-web-bundle/manifest-contract' +import { + loadBundledMobileWebBundle, + type BundledMobileWebBundle +} from '../../bundled-mobile-web-bundle' +import { isClientDisconnectedError } from '../../orca-runtime-core' +import { defineMethod, InvalidArgumentError, type RpcContext } from '../core' +import { + readMobileWebBundleAssetChunk, + verifyMobileWebBundleAsset +} from './mobile-web-bundle-asset-reader' +import { + acquireMobileWebBundleReadSlot, + mobileWebBundleReadBucket +} from './mobile-web-bundle-read-admission' + +/** The code IS the message: `InvalidArgumentError` carries no data field, so the message is the only + * place a stable code can travel, and a client must be able to branch without matching prose. */ +function bundleError(code: MobileWebBundleErrorCode): InvalidArgumentError { + return new InvalidArgumentError(code) +} + +function requireBundle(): BundledMobileWebBundle { + const bundle = loadBundledMobileWebBundle() + if (!bundle) { + throw bundleError('mobile_web_bundle_unavailable') + } + return bundle +} + +function abortIfDisconnected(ctx: RpcContext): void { + if (ctx.signal?.aborted) { + throw new Error('client_disconnected') + } +} + +/** Every other way a read can fail — the asset unlinked, unreadable, or shorter than the manifest + * promised — is one thing to a client: this bundle no longer matches the manifest it was handed. + * The host path stays on the host; the reply carries only the code. */ +function asContractError(error: unknown, path: string): unknown { + if (error instanceof InvalidArgumentError || isClientDisconnectedError(error)) { + return error + } + console.warn(`[mobile-web-bundle] read failed for ${path}:`, error) + return bundleError('mobile_web_bundle_asset_changed') +} + +/** Exact match against a manifest member. `path` is never joined, normalised, or prefix-matched, so + * traversal is not mitigated here — it is unreachable. */ +function findAsset(bundle: BundledMobileWebBundle, path: string): MobileWebBundleAsset { + const asset = bundle.manifest.assets.find((candidate) => candidate.path === path) + if (!asset) { + throw bundleError('mobile_web_bundle_asset_unknown') + } + return asset +} + +/** Alignment is against the size the manifest reply advertised, which the contract deliberately + * leaves off `offset` so the host can shrink the chunk without a client release. Offset 0 is always + * in range, so a zero-byte asset is still fetchable and still reports eof. */ +function assertOffsetAddressesAChunk(offset: number, asset: MobileWebBundleAsset): void { + if (offset % MOBILE_WEB_BUNDLE_CHUNK_BYTES !== 0) { + throw bundleError('mobile_web_bundle_offset_invalid') + } + if (offset > 0 && offset >= asset.byteLength) { + throw bundleError('mobile_web_bundle_offset_invalid') + } +} + +export const MOBILE_WEB_BUNDLE_METHODS = [ + defineMethod({ + name: MOBILE_WEB_BUNDLE_MANIFEST_METHOD, + params: null, + handler: async (): Promise => ({ + manifest: requireBundle().manifest, + chunkBytes: MOBILE_WEB_BUNDLE_CHUNK_BYTES + }) + }), + defineMethod({ + name: MOBILE_WEB_BUNDLE_CHUNK_METHOD, + params: MobileWebBundleChunkParamsSchema, + handler: async (params, ctx): Promise => { + const bundle = requireBundle() + // Checked before the asset lookup: a desktop that auto-updated mid-download must tell the + // client to restart from the manifest, not that its path went missing. + if (params.buildId !== bundle.manifest.buildId) { + throw bundleError('mobile_web_bundle_build_changed') + } + const asset = findAsset(bundle, params.path) + assertOffsetAddressesAChunk(params.offset, asset) + + const release = acquireMobileWebBundleReadSlot(mobileWebBundleReadBucket(ctx)) + if (!release) { + throw bundleError('mobile_web_bundle_read_limited') + } + try { + abortIfDisconnected(ctx) + if (!(await verifyMobileWebBundleAsset(bundle.root, bundle.manifest.buildId, asset))) { + throw bundleError('mobile_web_bundle_asset_changed') + } + abortIfDisconnected(ctx) + const data = await readMobileWebBundleAssetChunk( + bundle.root, + asset, + params.offset, + MOBILE_WEB_BUNDLE_CHUNK_BYTES + ) + return { + buildId: bundle.manifest.buildId, + path: asset.path, + offset: params.offset, + // The whole asset's length and hash, so one chunk describes the asset it belongs to. + assetByteLength: asset.byteLength, + sha256: asset.sha256, + dataBase64: data.toString('base64'), + eof: params.offset + data.byteLength >= asset.byteLength + } + } catch (error) { + throw asContractError(error, asset.path) + } finally { + release() + } + } + }) +] diff --git a/src/main/runtime/runtime-rpc/runtime-rpc-mobile-method-allowlist.ts b/src/main/runtime/runtime-rpc/runtime-rpc-mobile-method-allowlist.ts index ada9d7f0155..1bd86b2de28 100644 --- a/src/main/runtime/runtime-rpc/runtime-rpc-mobile-method-allowlist.ts +++ b/src/main/runtime/runtime-rpc/runtime-rpc-mobile-method-allowlist.ts @@ -175,6 +175,8 @@ export const MOBILE_RPC_METHOD_ALLOWLIST = new Set([ 'linear.updateIssue', 'markdown.readTab', 'markdown.saveTab', + 'mobileWeb.bundle.chunk', + 'mobileWeb.bundle.manifest', 'notifications.getMissedSince', 'notifications.registerPush', 'notifications.subscribe', diff --git a/src/shared/mobile-web-bundle/bundle-rpc-contract.test.ts b/src/shared/mobile-web-bundle/bundle-rpc-contract.test.ts index 4cc356026f3..a4c1abddbb2 100644 --- a/src/shared/mobile-web-bundle/bundle-rpc-contract.test.ts +++ b/src/shared/mobile-web-bundle/bundle-rpc-contract.test.ts @@ -10,7 +10,6 @@ import { MobileWebBundleChunkParamsSchema, MobileWebBundleChunkResultSchema, MobileWebBundleErrorCodeSchema, - MobileWebBundleManifestParamsSchema, MobileWebBundleManifestResultSchema, MOBILE_WEB_BUNDLE_CHUNK_BYTES, MOBILE_WEB_BUNDLE_CHUNK_METHOD, @@ -85,11 +84,6 @@ describe('MobileWebBundleErrorCodeSchema', () => { }) describe('mobileWeb.bundle.manifest payloads', () => { - it('takes null params', () => { - expect(MobileWebBundleManifestParamsSchema.safeParse(null).success).toBe(true) - expect(MobileWebBundleManifestParamsSchema.safeParse({}).success).toBe(false) - }) - it('carries a parsed manifest and the advertised chunk size', () => { const reply = { manifest: VALID_MANIFEST, chunkBytes: MOBILE_WEB_BUNDLE_CHUNK_BYTES } const parsed = MobileWebBundleManifestResultSchema.safeParse(reply) diff --git a/src/shared/mobile-web-bundle/bundle-rpc-contract.ts b/src/shared/mobile-web-bundle/bundle-rpc-contract.ts index 08b4aa88a72..4395a4e9d1c 100644 --- a/src/shared/mobile-web-bundle/bundle-rpc-contract.ts +++ b/src/shared/mobile-web-bundle/bundle-rpc-contract.ts @@ -10,6 +10,8 @@ import { * against the 1 MiB frame ceiling on both the WebSocket and relay transports. */ export const MOBILE_WEB_BUNDLE_CHUNK_BYTES = 48 * 1024 +/** Takes no params, and carries no params schema: the dispatcher substitutes `{}` for absent params, + * so a `z.null()` schema could never be satisfied. The method declares `params: null` host-side. */ export const MOBILE_WEB_BUNDLE_MANIFEST_METHOD = 'mobileWeb.bundle.manifest' export const MOBILE_WEB_BUNDLE_CHUNK_METHOD = 'mobileWeb.bundle.chunk' @@ -36,8 +38,6 @@ export const MOBILE_WEB_BUNDLE_ERROR_CODES = hostUnionArms export type MobileWebBundleManifestResult = z.infer export type MobileWebBundleChunkParams = z.infer export type MobileWebBundleChunkResult = z.infer diff --git a/src/shared/rpc-contract/rpc-params-catalog.generated.ts b/src/shared/rpc-contract/rpc-params-catalog.generated.ts index 3168406805c..7278e5eda8e 100644 --- a/src/shared/rpc-contract/rpc-params-catalog.generated.ts +++ b/src/shared/rpc-contract/rpc-params-catalog.generated.ts @@ -22,6 +22,7 @@ import { PairingGetEndpointsParamsSchema, PairingProvisionRelayParamsSchema } from '../mobile-relay-credential-contract' +import { MobileWebBundleChunkParamsSchema } from '../mobile-web-bundle/bundle-rpc-contract' import { pluginConsentRequestSchema } from '../plugins/plugin-consent-request' import { AccountsUnsubscribeParams, @@ -957,6 +958,8 @@ export const RPC_PARAMS_BY_METHOD = { 'linear.updateIssue': IssueUpdateOfLinearParams, 'markdown.readTab': ActivateTab, 'markdown.saveTab': SaveMarkdownTab, + 'mobileWeb.bundle.chunk': MobileWebBundleChunkParamsSchema, + 'mobileWeb.bundle.manifest': null, 'nativeChat.readSession': NativeChatSession, 'nativeChat.subscribe': NativeChatSession, 'nativeChat.unsubscribe': NativeChatUnsubscribe, From ffc812cdce619d4f96871b1a6b3ae83f8f1152ee Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 22:12:16 -0700 Subject: [PATCH 093/168] Reveal active workspaces with minimal filter changes (#21364) * Reveal workspaces by adjusting only blocking filters * Update runtime localization catalog * Preserve minimal reveal behavior across catalogs and folders --- .../src/components/sidebar/WorktreeList.tsx | 6 +- .../worktree-list/listing/use-filters.ts | 96 ++++++++++++++++++- .../navigation/use-reveal-requests.test.tsx | 26 +++-- .../navigation/use-reveal-requests.ts | 31 ++++-- .../src/i18n/en-runtime-required.json | 14 +-- src/renderer/src/i18n/locales/en.json | 4 +- 6 files changed, 151 insertions(+), 26 deletions(-) diff --git a/src/renderer/src/components/sidebar/WorktreeList.tsx b/src/renderer/src/components/sidebar/WorktreeList.tsx index ce6813d229f..d2f13e524ce 100644 --- a/src/renderer/src/components/sidebar/WorktreeList.tsx +++ b/src/renderer/src/components/sidebar/WorktreeList.tsx @@ -104,7 +104,8 @@ const WorktreeList = React.memo(function WorktreeList({ ) const agentSendTargetWorktreeId = useAgentSendTargetWorktreeId() - const { filterState, hasFilters, clearFilters } = useSidebarWorktreeFilters() + const { filterState, hasFilters, clearFilters, revealWorkspaceFilters } = + useSidebarWorktreeFilters() const sortedIds = useSidebarWorktreeSortOrder({ allWorktrees, repoMap, sortBy }) const manualOrderCatalog = useMemo( () => buildWorktreeManualOrderCatalog({ worktrees: allWorktrees, folderWorkspaces }), @@ -244,7 +245,8 @@ const WorktreeList = React.memo(function WorktreeList({ worktrees: allWorktrees, folderWorkspaces, hasFilters, - clearFilters + clearFilters, + revealWorkspaceFilters }) const filtersHideAllRows = shouldFiltersHideAllRows({ diff --git a/src/renderer/src/components/sidebar/worktree-list/listing/use-filters.ts b/src/renderer/src/components/sidebar/worktree-list/listing/use-filters.ts index f04e57194af..37bc57be8de 100644 --- a/src/renderer/src/components/sidebar/worktree-list/listing/use-filters.ts +++ b/src/renderer/src/components/sidebar/worktree-list/listing/use-filters.ts @@ -1,7 +1,30 @@ import { useCallback, useMemo } from 'react' import { useAppStore } from '@/store' import { DEFAULT_SHOW_SLEEPING_WORKSPACES } from '../../../../../../shared/constants' -import { computeClearFilterActions, sidebarHasActiveFilters } from '../../visible-worktrees' +import { + computeClearFilterActions, + sidebarHasActiveFilters, + isAutomationGeneratedWorkspace, + isCliCreatedWorkspace, + isDetachedHeadWorkspace, + isSleepingSweepExemptWorkspace +} from '../../visible-worktrees' +import type { Worktree } from '../../../../../../shared/worktree/types' +import { + getWorktreeExecutionHostId, + getSettingsFocusedExecutionHostId +} from '../../../../../../shared/execution-host' +import { isDefaultBranchWorkspace } from '../../default-branch-workspace' +import { + getPairedDeviceIdsByEnvironment, + isWorkspaceFromOtherDevice +} from '../../workspace-creator-visibility' +import { getAgentStatusEpochNow } from '@/lib/agent-status-epoch-clock' +import { getWorktreeIdsWithLiveAgent, isInactiveWorkspace } from '@/lib/worktree-activity-state' +import { + getVisibleWorktreeBrowserActivityTabs, + getVisibleWorktreeTerminalActivityTabs +} from '../../visible-worktree-activity-inputs' export type SidebarWorktreeFilters = ReturnType @@ -32,6 +55,70 @@ export function useSidebarWorktreeFilters() { const setFilterRepoIds = useAppStore((s) => s.setFilterRepoIds) const setVisibleWorkspaceHostIds = useAppStore((s) => s.setVisibleWorkspaceHostIds) + const revealWorkspaceFilters = useCallback((worktree: Worktree) => { + const state = useAppStore.getState() + const repo = state.repos.find((candidate) => candidate.id === worktree.repoId) + const targetHostId = getWorktreeExecutionHostId( + worktree, + repo, + getSettingsFocusedExecutionHostId(state.settings) + ) + + if (state.filterRepoIds.length > 0 && !state.filterRepoIds.includes(worktree.repoId)) { + state.setFilterRepoIds([...state.filterRepoIds, worktree.repoId]) + } + const visibleHostIds = state.visibleWorkspaceHostIds + const scopedHostIds = + visibleHostIds ?? (state.workspaceHostScope === 'all' ? null : [state.workspaceHostScope]) + if (scopedHostIds && !scopedHostIds.includes(targetHostId)) { + state.setVisibleWorkspaceHostIds([...scopedHostIds, targetHostId]) + } + if (state.hideDefaultBranchWorkspace && isDefaultBranchWorkspace(worktree)) { + state.setHideDefaultBranchWorkspace(false) + } + if (state.hideAutomationGeneratedWorkspaces && isAutomationGeneratedWorkspace(worktree)) { + state.setHideAutomationGeneratedWorkspaces(false) + } + if (state.hideCliCreatedWorkspaces && isCliCreatedWorkspace(worktree)) { + state.setHideCliCreatedWorkspaces(false) + } + if (state.hideDetachedHeadWorkspaces && isDetachedHeadWorkspace(worktree)) { + state.setHideDetachedHeadWorkspaces(false) + } + if (state.hideWorkspacesFromOtherDevices) { + const pairedDeviceIds = getPairedDeviceIdsByEnvironment( + state.runtimeEnvironments, + state.runtimeStatusByEnvironmentId + ) + if (isWorkspaceFromOtherDevice(worktree, pairedDeviceIds)) { + state.setHideWorkspacesFromOtherDevices(false) + } + } + if (!state.showSleepingWorkspaces) { + const tabsByWorktree = getVisibleWorktreeTerminalActivityTabs(state.tabsByWorktree) + const browserTabsByWorktree = getVisibleWorktreeBrowserActivityTabs( + state.browserTabsByWorktree + ) + const liveAgentWorktrees = getWorktreeIdsWithLiveAgent( + state.agentStatusByPaneKey, + tabsByWorktree, + getAgentStatusEpochNow(state.agentStatusEpoch) + ) + if ( + !isSleepingSweepExemptWorkspace(worktree, state.alwaysShowDefaultBranchWorkspace) && + isInactiveWorkspace( + worktree.id, + tabsByWorktree, + state.ptyIdsByTabId, + browserTabsByWorktree, + liveAgentWorktrees + ) + ) { + state.setShowSleepingWorkspaces(true) + } + } + }, []) + // Why: count hideDefaultBranchWorkspace as a filter so the Clear Filters escape hatch stays reachable when it alone empties the list. const filterState = useMemo( () => ({ @@ -102,5 +189,10 @@ export function useSidebarWorktreeFilters() { filterState ]) - return { filterState, hasFilters: sidebarHasActiveFilters(filterState), clearFilters } + return { + filterState, + hasFilters: sidebarHasActiveFilters(filterState), + clearFilters, + revealWorkspaceFilters + } } diff --git a/src/renderer/src/components/sidebar/worktree-list/navigation/use-reveal-requests.test.tsx b/src/renderer/src/components/sidebar/worktree-list/navigation/use-reveal-requests.test.tsx index f31b2bc9437..36b86ab0e9a 100644 --- a/src/renderer/src/components/sidebar/worktree-list/navigation/use-reveal-requests.test.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/navigation/use-reveal-requests.test.tsx @@ -79,6 +79,7 @@ beforeEach(() => { sortOrder: 1, lastActivityAt: 1 } + const clearFilters = vi.fn() args = { groupBy: 'repo', renderedSidebarRowKeys: new Set(), @@ -90,7 +91,8 @@ beforeEach(() => { worktrees: [worktree], folderWorkspaces: [], hasFilters: true, - clearFilters: vi.fn() + clearFilters, + revealWorkspaceFilters: clearFilters } }) @@ -103,7 +105,9 @@ describe('revealing a filtered workspace', () => { it('explains the filter reset and leaves filters intact when dismissed', async () => { await render() await act(async () => requestScrollToCurrentWorkspaceReveal()) - expect(document.body.textContent).toContain('Revealing it will clear your sidebar filters.') + expect(document.body.textContent).toContain( + 'Revealing it will adjust only the filters hiding it.' + ) expect(args.clearFilters).not.toHaveBeenCalled() expect(state.revealWorktreeInSidebar).not.toHaveBeenCalled() await click('Keep filters') @@ -111,13 +115,23 @@ describe('revealing a filtered workspace', () => { expect(state.revealWorktreeInSidebar).not.toHaveBeenCalled() }) + it('delegates to the minimal filter revealer when provided', async () => { + const revealWorkspaceFilters = vi.fn() + args = { ...args, revealWorkspaceFilters } + await render() + await act(async () => requestScrollToCurrentWorkspaceReveal()) + await click('Adjust filters and reveal') + expect(revealWorkspaceFilters).toHaveBeenCalledWith(args.worktrees[0]) + expect(args.clearFilters).not.toHaveBeenCalled() + }) + it('clears filters and reveals on the original execution host only after confirmation', async () => { await render() await act(async () => { requestScrollToCurrentWorkspaceReveal() requestScrollToCurrentWorkspaceReveal() }) - await click('Clear filters and reveal') + await click('Adjust filters and reveal') expect(args.clearFilters).toHaveBeenCalledTimes(1) expect(state.revealWorktreeInSidebar).toHaveBeenCalledWith('wt-1', { behavior: 'smooth', @@ -174,7 +188,7 @@ describe('revealing a filtered workspace', () => { await act(async () => requestScrollToCurrentWorkspaceReveal()) args = { ...args, visibleWorktrees: args.worktrees } await render() - await click('Clear filters and reveal') + await click('Adjust filters and reveal') expect(args.clearFilters).not.toHaveBeenCalled() expect(state.revealWorktreeInSidebar).toHaveBeenCalledTimes(1) }) @@ -184,7 +198,7 @@ describe('revealing a filtered workspace', () => { await act(async () => requestScrollToCurrentWorkspaceReveal()) args = { ...args, currentSidebarWorktreeId: 'wt-2' } await render() - await click('Clear filters and reveal') + await click('Adjust filters and reveal') expect(args.clearFilters).not.toHaveBeenCalled() expect(state.revealWorktreeInSidebar).not.toHaveBeenCalled() }) @@ -219,7 +233,7 @@ describe('revealing a filtered workspace', () => { await act(async () => requestScrollToCurrentWorkspaceRevealAndRename()) expect(args.clearFilters).not.toHaveBeenCalled() if (filtered) { - await click('Clear filters and reveal') + await click('Adjust filters and reveal') expect(args.clearFilters).toHaveBeenCalledTimes(1) } else { expect(document.querySelector('[role="dialog"]')).toBeNull() diff --git a/src/renderer/src/components/sidebar/worktree-list/navigation/use-reveal-requests.ts b/src/renderer/src/components/sidebar/worktree-list/navigation/use-reveal-requests.ts index 33841f51b6c..ebe5d695b6d 100644 --- a/src/renderer/src/components/sidebar/worktree-list/navigation/use-reveal-requests.ts +++ b/src/renderer/src/components/sidebar/worktree-list/navigation/use-reveal-requests.ts @@ -41,6 +41,7 @@ export function useSidebarRevealRequests(args: { folderWorkspaces: readonly FolderWorkspace[] hasFilters: boolean clearFilters: () => void + revealWorkspaceFilters: (worktree: Worktree) => void }): void { const { groupBy, @@ -53,7 +54,8 @@ export function useSidebarRevealRequests(args: { worktrees, folderWorkspaces, hasFilters, - clearFilters + clearFilters, + revealWorkspaceFilters } = args const setGroupBy = useAppStore((s) => s.setGroupBy) const pendingRevealSidebarRow = useAppStore((s) => s.pendingRevealSidebarRow) @@ -80,15 +82,29 @@ export function useSidebarRevealRequests(args: { return } if (!renderedSidebarRowKeys.has(rowKey) && hasFilters) { - clearFilters() + const target = getKnownSidebarWorktreeById( + rowKey, + worktreeMap, + folderWorkspaces, + worktrees, + currentSidebarExecutionHostId + ) + if (target) { + revealWorkspaceFilters(target) + } } }, [ clearFilters, groupBy, hasFilters, + currentSidebarExecutionHostId, + folderWorkspaces, pendingRevealSidebarRow, renderedSidebarRowKeys, - setGroupBy + setGroupBy, + worktreeMap, + worktrees, + revealWorkspaceFilters ]) const handleRevealCurrentWorkspaceRequest = useCallback( @@ -139,9 +155,9 @@ export function useSidebarRevealRequests(args: { title: translate('sidebar.revealFiltered.title', 'Reveal hidden workspace?'), description: translate( 'sidebar.revealFiltered.description', - 'The active workspace is hidden in the sidebar. Revealing it will clear your sidebar filters.' + 'The active workspace is hidden in the sidebar. Revealing it will adjust only the filters hiding it.' ), - confirmLabel: translate('sidebar.revealFiltered.confirm', 'Clear filters and reveal'), + confirmLabel: translate('sidebar.revealFiltered.confirm', 'Adjust filters and reveal'), cancelLabel: translate('sidebar.revealFiltered.cancel', 'Keep filters') }) } finally { @@ -164,7 +180,7 @@ export function useSidebarRevealRequests(args: { latest.visibleFolderWorkspaces ) ) { - latest.clearFilters() + revealWorkspaceFilters(activeWorktree) } } revealWorktreeInSidebar(currentSidebarWorktreeId, { @@ -185,7 +201,8 @@ export function useSidebarRevealRequests(args: { visibleFolderWorkspaces, revealWorktreeInSidebar, worktreeMap, - worktrees + worktrees, + revealWorkspaceFilters ] ) diff --git a/src/renderer/src/i18n/en-runtime-required.json b/src/renderer/src/i18n/en-runtime-required.json index 5e1c5bb3a84..8fe443f65de 100644 --- a/src/renderer/src/i18n/en-runtime-required.json +++ b/src/renderer/src/i18n/en-runtime-required.json @@ -73,19 +73,19 @@ "f5a6b38a14": "sheet" }, "NativeChatResumeOnRestartModal": { + "continueRefused_one": "{{value0}} chat could not be continued. Open it to continue manually.", + "continueRefused_other": "{{value0}} chats could not be continued. Open them to continue manually.", + "continueUnconfirmed_one": "Continuation delivery is unconfirmed for {{value0}} chat. Open it to check before sending another message.", + "continueUnconfirmed_other": "Continuation delivery is unconfirmed for {{value0}} chats. Open them to check before sending another message.", "manyAgents": "{{value0}} agents", "oneAgent": "1 agent", "projects": "Folder workspaces", "reconnectAgent": "Reconnect {{value0}} chat", - "resume": "Reconnect", - "continueUnconfirmed_one": "Continuation delivery is unconfirmed for {{value0}} chat. Open it to check before sending another message.", - "continueUnconfirmed_other": "Continuation delivery is unconfirmed for {{value0}} chats. Open them to check before sending another message.", + "reconnectRefused_one": "{{value0}} chat could not be reconnected. You can still open it normally.", + "reconnectRefused_other": "{{value0}} chats could not be reconnected. You can still open them normally.", "reconnectUnconfirmed_one": "Reconnection is unconfirmed for {{value0}} chat. You can still open it normally.", "reconnectUnconfirmed_other": "Reconnection is unconfirmed for {{value0}} chats. You can still open them normally.", - "continueRefused_one": "{{value0}} chat could not be continued. Open it to continue manually.", - "continueRefused_other": "{{value0}} chats could not be continued. Open them to continue manually.", - "reconnectRefused_one": "{{value0}} chat could not be reconnected. You can still open it normally.", - "reconnectRefused_other": "{{value0}} chats could not be reconnected. You can still open them normally." + "resume": "Reconnect" }, "NewWorkspaceComposerCard": { "0e587e31fb": "yaml", diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index 00c2bfe2514..6ded01db780 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -2,8 +2,8 @@ "sidebar": { "revealFiltered": { "title": "Reveal hidden workspace?", - "description": "The active workspace is hidden in the sidebar. Revealing it will clear your sidebar filters.", - "confirm": "Clear filters and reveal", + "description": "The active workspace is hidden in the sidebar. Revealing it will adjust only the filters hiding it.", + "confirm": "Adjust filters and reveal", "cancel": "Keep filters" } }, From 945ea33541d8b47dc51f973aff5dff2fbc34a1ae Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 22:12:46 -0700 Subject: [PATCH 094/168] Revert "fix(editor): evict stale mirrored file tabs (#21363)" (#21368) This reverts commit 07e8c851b8b03651468459d9a63c285329e6e105. The eviction keys on `selector_not_found`, which this repo documents twice as UNKNOWN rather than absence: - `remote-browser-stream-errors.ts`: "it means 'I could not resolve this right now', which is UNKNOWN, not proof the target is gone. Its producer is a live worktree scan behind a 1s-TTL cache ... a slow scan can surface it transiently. Treating that as permanent would strand the pane forever, which is the exact bug this file exists to prevent." - `web-runtime-session-tab-lifecycle.ts`, added by #21277: "'selector_not_found' is a transient worktree resolver state (e.g. during scans or cache warm-up) and must not become a durable close tombstone." Two unambiguous absence codes exist for this purpose -- `tab_not_found` and `terminal_tab_not_found` -- and #21277 had just finished excluding `selector_not_found` from them. This keyed on the excluded one. Consequences, after roughly 3.75s of retries: 1. `closeFile` deletes `editorDrafts[fileId]` with no dirty check and no confirmation, so a transient resolver blip discards unsaved edits. 2. `closeFile` calls `notifyHostOfMirroredEditorClose`, so the host closes its copy too -- the eviction is not local and not recoverable. The `!ownerNotReady` guard does not cover this: `ownerNotReady` means the host is still connecting, while `selector_not_found` is emitted for a cold resolver cache or an unhydrated catalog, which is a different state. #21041 is still open. A correct fix keys on the two definitive absence codes, refuses to evict a tab that has a draft, and has a test proving a dirty mirrored tab survives `selector_not_found`. --- .../editor/useEditorPanelContentState.ts | 3 +- .../useEditorPanelFileLoadRetry.test.tsx | 32 ------------------- .../editor/useEditorPanelFileLoadRetry.ts | 19 ----------- 3 files changed, 1 insertion(+), 53 deletions(-) diff --git a/src/renderer/src/components/editor/useEditorPanelContentState.ts b/src/renderer/src/components/editor/useEditorPanelContentState.ts index 93c33d0961c..ec1a0fc6b1b 100644 --- a/src/renderer/src/components/editor/useEditorPanelContentState.ts +++ b/src/renderer/src/components/editor/useEditorPanelContentState.ts @@ -1,6 +1,6 @@ import { useCallback, useLayoutEffect, useRef, useState } from 'react' import type { OpenFile } from '@/store/slices/editor' -import { useAppStore } from '@/store' +import type { useAppStore } from '@/store' import type { DiffContent, FileContent } from './editor-panel-content-types' import { useEditorPanelExternalContentEvents, @@ -194,7 +194,6 @@ export function useEditorPanelContentState({ fileLoadRetryAttemptsRef, loadFileContent, openFilesRef, - closeFile: useAppStore.getState().closeFile, setFileContents }) diff --git a/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.test.tsx b/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.test.tsx index fe40142c645..6a29017d654 100644 --- a/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.test.tsx +++ b/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.test.tsx @@ -49,7 +49,6 @@ function Harness({ attemptsRef, isVisible = true, loadFileContent, - closeFile = vi.fn(), setFileContents }: { file: OpenFile @@ -57,7 +56,6 @@ function Harness({ attemptsRef: { current: Record } isVisible?: boolean loadFileContent: (filePath: string, id: string) => Promise - closeFile?: (fileId: string) => void setFileContents: ( updater: (prev: Record) => Record ) => void @@ -68,7 +66,6 @@ function Harness({ fileLoadRetryAttemptsRef: attemptsRef, loadFileContent: loadFileContent as never, openFilesRef: { current: [file] }, - closeFile, setFileContents: setFileContents as never }) return null @@ -106,35 +103,6 @@ describe('useEditorPanelFileLoadRetry — owner-not-ready bounding (#6648)', () expect(shouldRetryFileLoadError('Access denied: outside allowed directories')).toBe(false) }) - it('evicts a mirrored tab after selector resolution stays missing', () => { - const file = makeFile({ mirroredFromRuntimeSession: true }) - const attemptsRef = { current: { [file.id]: 3 } } - const closeFile = vi.fn() - const fileContents: Record = { - [file.id]: { content: '', isBinary: false, loadError: 'selector_not_found' } - } - - container = document.createElement('div') - document.body.appendChild(container) - root = createRoot(container) - act(() => { - root?.render( - undefined)} - closeFile={closeFile} - setFileContents={(updater) => { - updater(fileContents) - }} - /> - ) - }) - - expect(closeFile).toHaveBeenCalledWith(file.id) - }) - it('does not spend retry budget when hiding cancels a pending retry', () => { setTimeoutSpy.mockRestore() setTimeoutSpy = vi.spyOn(window, 'setTimeout') diff --git a/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.ts b/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.ts index 57a9483218e..9fb96ad80aa 100644 --- a/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.ts +++ b/src/renderer/src/components/editor/useEditorPanelFileLoadRetry.ts @@ -7,7 +7,6 @@ import { } from './editor-panel-content-types' const FILE_LOAD_RETRY_DELAYS_MS = [250, 1000, 2500] -const noopCloseFile = (): void => {} // Why: a remote host can take a while to finish connecting. The owner-not-ready // check is a pure local store read (it throws before any network call until the // SSH repo hydrates), so poll it at a steady cadence — but cap the wait so a @@ -31,14 +30,9 @@ type UseEditorPanelFileLoadRetryParams = { relativePath?: string ) => Promise openFilesRef: MutableRefObject - closeFile?: (fileId: string) => void setFileContents: Dispatch>> } -function isSelectorNotFoundError(message: string): boolean { - return message.trim().toLowerCase() === 'selector_not_found' -} - export function shouldRetryFileLoadError(message: string): boolean { // Terminal: the owner-not-ready budget is spent; only an explicit Retry should // restart it, never the automatic backoff. @@ -60,7 +54,6 @@ export function useEditorPanelFileLoadRetry({ fileLoadRetryAttemptsRef, loadFileContent, openFilesRef, - closeFile = noopCloseFile, setFileContents }: UseEditorPanelFileLoadRetryParams): void { const activeFileLoadRetryId = activeFile?.id ?? null @@ -82,16 +75,6 @@ export function useEditorPanelFileLoadRetry({ ? OWNER_NOT_READY_RETRY_LIMIT : FILE_LOAD_RETRY_DELAYS_MS.length if (retryCount >= retryLimit) { - if ( - !ownerNotReady && - isSelectorNotFoundError(activeFileLoadError) && - activeFile?.mirroredFromRuntimeSession === true - ) { - // A host-mirrored file whose worktree stays unresolvable after the normal - // read retries is stale; evict it before snapshots can select it again. - closeFile(activeFileLoadRetryId) - return - } // Why: the remote host never finished connecting. Replace the transient // "still connecting" text with a truthful terminal message so it does not // look like it is still retrying; Retry starts a fresh budget (#6648). @@ -143,8 +126,6 @@ export function useEditorPanelFileLoadRetry({ }, [ activeFileLoadRetryId, activeFileLoadError, - activeFile?.mirroredFromRuntimeSession, - closeFile, fileLoadRetryAttemptsRef, loadFileContent, openFilesRef, From 8d2f16856f1ce5f29f6f00523ff75b85431c4093 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 17 Sep 2026 22:12:55 -0700 Subject: [PATCH 095/168] fix(session): scope agent resume to the host that captured the session (#21288) * fix(session): scope agent resume to the host that captured the session A provider session id names a transcript in one machine's agent state directory. Nothing in the resume path compared that machine against the one the resume executes on, so a record captured on host A reached a `--resume` run on host B, which answers `No conversation found with session ID`. Three things make the drift reachable: `worktreeId` is `repoId::path` with no host component, sleeping records are `'sleepingAgentKeyed'` so boot-time host-contention parking never arbitrates them and every partition merges into one map without retaining provenance, and both issuers resolve their launch target from the current catalog. Both issuers are gated. The activation sweep hands `quit`/`live` records whose pane still exists to the pane's own cold restore, so gating the sweep alone changed nothing in the SSH lane. Declines rather than guesses: the record is preserved and remains resumable by hand. A refused resume is recoverable, a forked transcript is not. The predicate fails open on anything it cannot positively rule out -- an unstamped record, an empty stamp, or a `runtime:` host, which a paired client uses to relabel its host's own SSH workspaces. The cold-restore gate consults both the pane's transport and the catalog. The transport alone was racy: it is unresolved on an early reattach frame, and that frame is exactly when a wrong resume escaped. * docs(session): name the inverted fail-open direction at the resume gate * fix(session): keep an unresolved catalog out of the resume host verdict The worktree form of the resume gate resolved the current host through getExecutionHostIdForWorktree, which answers 'local' for a worktree the catalog has no row for. Read as a host, that made every SSH-stamped record look foreign until its repo row landed, contradicting the module's own contract that it reports only a positively-known disagreement. Add getKnownExecutionHostIdForWorktree, which returns null in that silence (no repo row for a git worktree, no folder-workspace row for a folder workspace), and route the gate through it; the pair form already fails open on a null host. The routing resolver keeps its default unchanged. The CI red on the control case was a separate spec race: the ledger wait returned as soon as the ledger was non-empty, and it already held the first launch's `--version` probe, so the control read two probes and gave up before the cold-restore had typed `--resume` (the failure screenshot shows the command running in the pane). The spec now reads only the lines the relaunch appended, anchors on the relaunch's PTY binding and its own probe, and then waits for `--resume` for the control case or a bounded grace for the refusal case. --- config/scripts/run-ssh-docker-e2e.mjs | 1 + .../cold-restore-resume-startup.ts | 23 ++ ...agent-session-execution-host-scope.test.ts | 239 ++++++++++++++ .../src/lib/resume-sleeping-agent-session.ts | 9 + .../sleeping-record-execution-host-scope.ts | 78 +++++ .../src/lib/worktree-runtime-owner.test.ts | 37 +++ .../src/lib/worktree-runtime-owner.ts | 47 ++- ...-stale-resume-execution-host-scope.spec.ts | 306 ++++++++++++++++++ 8 files changed, 733 insertions(+), 7 deletions(-) create mode 100644 src/renderer/src/lib/resume-sleeping-agent-session-execution-host-scope.test.ts create mode 100644 src/renderer/src/lib/sleeping-record-execution-host-scope.ts create mode 100644 tests/e2e/ssh-stale-resume-execution-host-scope.spec.ts diff --git a/config/scripts/run-ssh-docker-e2e.mjs b/config/scripts/run-ssh-docker-e2e.mjs index fc0d628ab78..2eea3575fde 100644 --- a/config/scripts/run-ssh-docker-e2e.mjs +++ b/config/scripts/run-ssh-docker-e2e.mjs @@ -78,6 +78,7 @@ const result = spawnSync( 'tests/e2e/ssh-reconnect-tab-destruction.spec.ts', 'tests/e2e/ssh-restart-tab-accumulation.spec.ts', 'tests/e2e/ssh-skill-installation.spec.ts', + 'tests/e2e/ssh-stale-resume-execution-host-scope.spec.ts', 'tests/e2e/ssh-terminal-window-wake-stale-grid-repro.spec.ts', '--config', 'tests/playwright.config.ts', diff --git a/src/renderer/src/components/terminal-pane/pty-connection/cold-restore-resume-startup.ts b/src/renderer/src/components/terminal-pane/pty-connection/cold-restore-resume-startup.ts index c719e4a83ef..d15231fca7d 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection/cold-restore-resume-startup.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection/cold-restore-resume-startup.ts @@ -2,6 +2,10 @@ import { useAppStore } from '@/store' import { createBrowserUuid } from '@/lib/browser-uuid' import { buildAgentResumeStartupPlan } from '@/lib/tui-agent-startup' import { resolveAgentResumeLaunchTarget } from '@/lib/agent-resume-launch-target' +import { + agentResumeOriginNamesAnotherExecutionHost, + sleepingRecordNamesAnotherExecutionHost +} from '@/lib/sleeping-record-execution-host-scope' import { resolveTuiAgentLaunchArgs, resolveTuiAgentLaunchEnv @@ -37,6 +41,25 @@ export function bindBuildColdRestoreAgentResumeStartup(session: ConnectPanePtySe if (!providerSession) { return null } + // Why: this is the second issuer of `--resume`, and the one that handles a quit/live record + // whose pane still exists — the sweep hands those here rather than launching them. A session id + // names a transcript on the machine that captured it, so replaying one over a pane now attached + // to a different host answers `No conversation found`. Returning null leaves the pane with a + // plain shell and the record intact, which the user can resume by hand. + // + // Two sources are consulted because either can be the one that knows. `session.executionHostId` + // is the pane's own transport and is authoritative when set, but it is still unresolved on an + // early reattach frame — and failing open on that frame is precisely when a wrong resume slips + // out. The catalog's answer for the record's worktree covers that window. + if ( + agentResumeOriginNamesAnotherExecutionHost( + useLiveEntry ? entry.connectionId : sleepingRecord?.connectionId, + session.executionHostId + ) || + (sleepingRecord && sleepingRecordNamesAnotherExecutionHost(sleepingRecord, state)) + ) { + return null + } const matchingSleepingLaunchConfig = sleepingRecord?.launchConfig && (!useLiveEntry || diff --git a/src/renderer/src/lib/resume-sleeping-agent-session-execution-host-scope.test.ts b/src/renderer/src/lib/resume-sleeping-agent-session-execution-host-scope.test.ts new file mode 100644 index 00000000000..54b5459a8cf --- /dev/null +++ b/src/renderer/src/lib/resume-sleeping-agent-session-execution-host-scope.test.ts @@ -0,0 +1,239 @@ +/** + * A provider session id names a transcript in ONE machine's agent state directory. Replaying a + * record captured on host A as a `--resume` executed on host B answers + * `No conversation found with session ID: ` at best, and at worst reopens an unrelated + * transcript that happens to share the id. + * + * Nothing in the resume path was host-scoped: `worktreeId` is `repoId::path` with no host component + * (shared/worktree/host-qualified-identity.ts), sleeping records are `'sleepingAgentKeyed'` so the + * boot-time host-contention parking never arbitrates them and every partition's records merge into + * one map, and `launchSleepingAgentSession` resolves its launch target from the *current* catalog. + * + * Both directions matter. The sweep must decline when the record names another machine, and it must + * still resume everything it cannot positively rule out — a gate that refuses on absent evidence + * would strand every record captured before the stamp existed. + */ +import { afterEach, describe, expect, it } from 'vitest' +import type { SleepingAgentSessionRecord } from '../../../shared/agent-session-resume' +import type { ExecutionHostId } from '../../../shared/execution-host' +import { useAppStore } from '@/store' +import { makeWorktree, TEST_REPO } from '@/store/slices/store-test-helpers' +import { resumeSleepingAgentSessionsForWorktree } from './resume-sleeping-agent-session' +import { + agentResumeOriginNamesAnotherExecutionHost, + sleepingRecordNamesAnotherExecutionHost +} from './sleeping-record-execution-host-scope' +import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner' + +const initialAppStoreState = useAppStore.getState() + +const TARGET_ID = 'openclaw' +const REMOTE_PATH = '/home/neil/projects/orca-test123' +const WORKTREE_ID = `repo-1::${REMOTE_PATH}` +const SESSION_ID = '87987465-66f6-4967-bf3f-0659565cbcc5' + +afterEach(() => { + useAppStore.setState(initialAppStoreState, true) +}) + +function makeRecord( + overrides: Partial = {} +): SleepingAgentSessionRecord { + return { + paneKey: 'tab-1:leaf-1', + tabId: 'tab-1', + worktreeId: WORKTREE_ID, + agent: 'claude', + providerSession: { key: 'session_id', id: SESSION_ID }, + prompt: 'finish the task', + state: 'working', + origin: 'quit', + capturedAt: 1, + updatedAt: 1, + ...overrides + } +} + +/** A catalog that resolves WORKTREE_ID to exactly `hostId`, with no tab rows for it. */ +function catalogOwnedBy(hostId: ExecutionHostId): WorktreeRuntimeOwnerState { + const connectionId = hostId.startsWith('ssh:') + ? decodeURIComponent(hostId.slice('ssh:'.length)) + : undefined + return { + repos: [ + { + id: 'repo-1', + ...(connectionId ? { connectionId } : {}), + ...(hostId.startsWith('runtime:') ? { executionHostId: hostId } : {}) + } + ], + worktreesByRepo: { + 'repo-1': [makeWorktree({ id: WORKTREE_ID, repoId: 'repo-1', path: REMOTE_PATH, hostId })] + } + } +} + +describe('sleepingRecordNamesAnotherExecutionHost', () => { + it.each([ + ['an SSH record on a different SSH target', 'other-target', `ssh:${TARGET_ID}`], + ['an SSH record on the local host', TARGET_ID, 'local'], + ['a local-or-runtime record on an SSH host', null, `ssh:${TARGET_ID}`] + ] as const)('refuses %s', (_label, connectionId, hostId) => { + const record = makeRecord({ connectionId }) + expect(sleepingRecordNamesAnotherExecutionHost(record, catalogOwnedBy(hostId))).toBe(true) + }) + + it.each([ + ['the same SSH target', TARGET_ID, `ssh:${TARGET_ID}`], + ['a target id needing URI encoding', 'my host', 'ssh:my%20host'], + ['a local record on the local host', null, 'local'], + // A paired client renames its host's workspaces — including that host's SSH ones — into its own + // runtime namespace, so a runtime answer is no evidence about the machine holding the transcript. + ['an SSH record whose workspace now reads as a paired runtime', TARGET_ID, 'runtime:env-1'], + ['a local-or-runtime record on a paired runtime', null, 'runtime:env-1'] + ] as const)('allows %s', (_label, connectionId, hostId) => { + const record = makeRecord({ connectionId }) + expect(sleepingRecordNamesAnotherExecutionHost(record, catalogOwnedBy(hostId))).toBe(false) + }) + + it.each([ + ['never stamped', undefined], + ['stamped with whitespace', ' '] + ] as const)('fails open on a record %s', (_label, connectionId) => { + // #9030 leaves SSH orphans unstamped. Refusing on absent evidence would strand every record + // captured before the stamp existed, which is a worse failure than the one being fixed. + const record = makeRecord(connectionId === undefined ? {} : { connectionId }) + expect( + sleepingRecordNamesAnotherExecutionHost(record, catalogOwnedBy(`ssh:${TARGET_ID}`)) + ).toBe(false) + }) + + it.each([ + ['an SSH record', TARGET_ID], + ['a local-or-runtime record', null] + ] as const)( + 'fails open for %s when the catalog has no row for the worktree', + (_label, connectionId) => { + // The routing resolver answers `'local'` for a worktree it has no row for. Read as a host, that + // would make every SSH record look foreign until its repo row lands — a gate that never resumes + // yours is the inverse of the defect and worse. + const record = makeRecord({ connectionId }) + expect( + sleepingRecordNamesAnotherExecutionHost(record, { repos: [], worktreesByRepo: {} }) + ).toBe(false) + } + ) + + it('still refuses an SSH record once a repo row positively names the worktree local', () => { + const record = makeRecord({ connectionId: TARGET_ID }) + expect( + sleepingRecordNamesAnotherExecutionHost(record, { + repos: [{ id: 'repo-1' }], + worktreesByRepo: {} + }) + ).toBe(true) + }) +}) + +describe('agentResumeOriginNamesAnotherExecutionHost', () => { + // The pane cold-restore path asks the same question against the transport the pane is attached to + // rather than the catalog, so the host-pair form is exported and pinned separately. + it.each([ + ['an SSH origin against another SSH pane', TARGET_ID, 'ssh:elsewhere', true], + ['an SSH origin against a local pane', TARGET_ID, 'local', true], + ['a local origin against an SSH pane', null, `ssh:${TARGET_ID}`, true], + ['an SSH origin against its own pane', TARGET_ID, `ssh:${TARGET_ID}`, false], + ['a local origin against a local pane', null, 'local', false], + ['an SSH origin against a paired-runtime pane', TARGET_ID, 'runtime:env-1', false] + ] as const)('reports %s as %s', (_label, originConnectionId, hostId, expected) => { + expect(agentResumeOriginNamesAnotherExecutionHost(originConnectionId, hostId)).toBe(expected) + }) + + it.each([null, undefined])( + 'fails open when the pane has no resolved execution host (%s)', + (hostId) => { + // A pane whose owner is still unresolved is not evidence of a different machine. + expect(agentResumeOriginNamesAnotherExecutionHost(TARGET_ID, hostId)).toBe(false) + } + ) +}) + +/** The SSH workspace after its host has answered, so terminal-host authority is decided and the + * sweep is allowed to act. Without the hydration mark the sweep declines for an unrelated reason + * and every assertion below would pass vacuously. */ +function seedAnsweredSshWorkspace(...records: SleepingAgentSessionRecord[]): void { + useAppStore.setState({ + repos: [{ ...TEST_REPO, id: 'repo-1', path: '/home/neil/projects', connectionId: TARGET_ID }], + worktreesByRepo: { + 'repo-1': [ + makeWorktree({ + id: WORKTREE_ID, + repoId: 'repo-1', + path: REMOTE_PATH, + hostId: `ssh:${TARGET_ID}` + }) + ] + }, + tabsByWorktree: {}, + sleepingAgentSessionsByPaneKey: Object.fromEntries( + records.map((record) => [record.paneKey, record]) + ) + }) + useAppStore.getState().markRemoteWorkspaceHydrated(TARGET_ID) +} + +describe('the resume sweep under execution-host scope', () => { + it('declines a locally captured session id rather than issuing it on the SSH host', () => { + const record = makeRecord({ connectionId: null }) + seedAnsweredSshWorkspace(record) + + expect( + resumeSleepingAgentSessionsForWorktree(WORKTREE_ID), + 'issued --resume for a local session id against the SSH host' + ).toBe(0) + expect(useAppStore.getState().tabsByWorktree[WORKTREE_ID] ?? []).toHaveLength(0) + }) + + it('preserves the declined record so the session stays resumable by hand', () => { + const record = makeRecord({ connectionId: 'a-different-target' }) + seedAnsweredSshWorkspace(record) + + resumeSleepingAgentSessionsForWorktree(WORKTREE_ID) + resumeSleepingAgentSessionsForWorktree(WORKTREE_ID) + + // Declining is recoverable only if the record survives; deleting it on a host disagreement + // would destroy the user's only handle on that transcript. + expect(useAppStore.getState().sleepingAgentSessionsByPaneKey[record.paneKey]).toBe(record) + }) + + it('still resumes a session captured on the host that owns the workspace', () => { + const record = makeRecord({ connectionId: TARGET_ID }) + seedAnsweredSshWorkspace(record) + + expect(resumeSleepingAgentSessionsForWorktree(WORKTREE_ID)).toBe(1) + expect(useAppStore.getState().sleepingAgentSessionsByPaneKey[record.paneKey]).toBeUndefined() + }) + + it('still resumes a legacy record that names no host at all', () => { + const record = makeRecord() + seedAnsweredSshWorkspace(record) + + expect(resumeSleepingAgentSessionsForWorktree(WORKTREE_ID)).toBe(1) + }) + + it('declines only the foreign record and resumes its native sibling', () => { + const foreign = makeRecord({ + paneKey: 'tab-1:leaf-1', + tabId: 'tab-1', + connectionId: null, + providerSession: { key: 'session_id', id: 'session-from-the-laptop' } + }) + const native = makeRecord({ paneKey: 'tab-2:leaf-1', tabId: 'tab-2', connectionId: TARGET_ID }) + seedAnsweredSshWorkspace(foreign, native) + + expect(resumeSleepingAgentSessionsForWorktree(WORKTREE_ID)).toBe(1) + const state = useAppStore.getState() + expect(state.sleepingAgentSessionsByPaneKey[foreign.paneKey]).toBe(foreign) + expect(state.sleepingAgentSessionsByPaneKey[native.paneKey]).toBeUndefined() + }) +}) diff --git a/src/renderer/src/lib/resume-sleeping-agent-session.ts b/src/renderer/src/lib/resume-sleeping-agent-session.ts index 0610eb9cf8b..50fb378888f 100644 --- a/src/renderer/src/lib/resume-sleeping-agent-session.ts +++ b/src/renderer/src/lib/resume-sleeping-agent-session.ts @@ -21,6 +21,7 @@ import { type UnhydratedHostMirror } from './host-mirrored-pane-liveness' import { parkUntilHostMirrorHandleLands } from './host-mirror-handle-gap-wait' +import { sleepingRecordNamesAnotherExecutionHost } from './sleeping-record-execution-host-scope' import { resolveWorkspaceTerminalHostAuthority } from './workspace-terminal-host-authority' import { parkUntilHostSessionMirrorHydrates } from '@/runtime/host-session-mirror-hydration' @@ -262,6 +263,14 @@ export function resumeSleepingAgentSessionsForWorktree( state.clearSleepingAgentSession(record.paneKey) continue } + // Why this is a `continue` and not a clear: the id is a valid locator on the machine that + // captured it, so the record is evidence, not garbage — deleting it on the strength of a host + // disagreement would destroy the user's only handle on that transcript. Declining costs an + // automatic wake the user can re-issue by hand; issuing `--resume` on the wrong machine is + // `No conversation found` at best and a forked transcript at worst. + if (sleepingRecordNamesAnotherExecutionHost(record, currentState)) { + continue + } const unhydratedMirror = findUnhydratedHostMirrorForPane(record, currentState) if (unhydratedMirror) { // Why: pane ownership is undecidable until the mirror answers, and every diff --git a/src/renderer/src/lib/sleeping-record-execution-host-scope.ts b/src/renderer/src/lib/sleeping-record-execution-host-scope.ts new file mode 100644 index 00000000000..8581a690f65 --- /dev/null +++ b/src/renderer/src/lib/sleeping-record-execution-host-scope.ts @@ -0,0 +1,78 @@ +import type { SleepingAgentSessionRecord } from '../../../shared/agent-session-resume' +import { + parseExecutionHostId, + toSshExecutionHostId, + type ExecutionHostId +} from '../../../shared/execution-host' +import { + getKnownExecutionHostIdForWorktree, + type WorktreeRuntimeOwnerState +} from './worktree-runtime-owner' + +/** + * Does this record's `--resume` locator belong to a different machine than the one the resume would + * run on? + * + * A provider session id names a transcript in one machine's agent state directory, but nothing + * else in the resume path is host-scoped: `worktreeId` is `repoId::path` with no host component + * (shared/worktree/host-qualified-identity.ts), sleeping records are `'sleepingAgentKeyed'` so the + * boot-time host-contention parking never arbitrates them and every partition's records merge into + * one map, and `launchSleepingAgentSession` resolves its launch target from the *current* catalog. + * A record captured on host A therefore reaches a launch on host B, which answers + * `No conversation found with session ID`. + * + * Deliberately fails open. It reports only a positively-known disagreement about the machine, + * because the alternative — refusing whenever the hosts cannot be compared — would strand every + * legitimate resume whose capture predates the stamp. + * + * "Fail open" names a direction for THIS decision, never a house style, and the safe direction is + * inverted a few files away. Here the destructive act is *attempting* a resume — a wrong one can + * fork a transcript, which is unrecoverable, while a refusal keeps the record and the user can + * resume by hand. So an unhydrated catalog must not be read as a host verdict. In + * `workspace-session-terminal-buffers.ts` the destructive act is the opposite: declining to capture + * loses the only scrollback copy, so an unknown repo is treated as remote. Same window, opposite + * default, both correct. A reader pattern-matching one onto the other will get this backwards. + * + * The four unknowns this fails open on: + * + * - `undefined` is "never stamped", not "local" (#9030 leaves SSH orphans unstamped). + * - `null` is "local **or** paired runtime": a `remote:@@` PTY is stamped null too + * (agent-status-connection-ownership.ts), so null cannot rule a runtime host out — only an + * `ssh:` one, which is unambiguously another machine. + * - A current host of `runtime:*` is no evidence either way, because a paired client relabels its + * host's workspaces — including that host's own SSH ones — into its runtime namespace. + * - A current host of `null` is a catalog with no row for the worktree. The routing resolver + * answers `'local'` there, which is the right default for issuing an operation and would read + * here as a positive host — so the worktree form below asks the resolver that keeps the silence. + */ +export function agentResumeOriginNamesAnotherExecutionHost( + originConnectionId: string | null | undefined, + currentExecutionHostId: ExecutionHostId | null | undefined +): boolean { + if (originConnectionId === undefined) { + return false + } + const originTargetId = originConnectionId === null ? null : originConnectionId.trim() + if (originTargetId === '') { + return false + } + const currentHost = parseExecutionHostId(currentExecutionHostId) + if (!currentHost || currentHost.kind === 'runtime') { + return false + } + if (currentHost.kind === 'ssh') { + return originTargetId === null || toSshExecutionHostId(originTargetId) !== currentHost.id + } + return originTargetId !== null +} + +/** The worktree-scoped form the activation sweep asks, resolving the host from the catalog. */ +export function sleepingRecordNamesAnotherExecutionHost( + record: SleepingAgentSessionRecord, + state: WorktreeRuntimeOwnerState +): boolean { + return agentResumeOriginNamesAnotherExecutionHost( + record.connectionId, + getKnownExecutionHostIdForWorktree(state, record.worktreeId) + ) +} diff --git a/src/renderer/src/lib/worktree-runtime-owner.test.ts b/src/renderer/src/lib/worktree-runtime-owner.test.ts index 995d7c15715..02ea0682e6b 100644 --- a/src/renderer/src/lib/worktree-runtime-owner.test.ts +++ b/src/renderer/src/lib/worktree-runtime-owner.test.ts @@ -3,6 +3,7 @@ import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants' import { getExplicitRuntimeEnvironmentIdForWorktree, getExecutionHostIdForWorktree, + getKnownExecutionHostIdForWorktree, getRuntimeEnvironmentIdForWorktree, getRuntimeSessionMirrorEnvironmentIds, getSettingsForWorktreeRuntimeOwner, @@ -610,3 +611,39 @@ describe('active workspace host selection', () => { ) }) }) + +describe('getKnownExecutionHostIdForWorktree', () => { + const emptyCatalog: WorktreeRuntimeOwnerState = { repos: [], worktreesByRepo: {} } + + it('reports silence, not local, for a git worktree with no repo row', () => { + expect(getKnownExecutionHostIdForWorktree(emptyCatalog, 'missing-repo::wt')).toBeNull() + // The routing form keeps substituting the default in the same state. + expect(getExecutionHostIdForWorktree(emptyCatalog, 'missing-repo::wt')).toBe('local') + }) + + it('reports silence for a folder workspace with no folder-workspace row', () => { + expect(getKnownExecutionHostIdForWorktree(emptyCatalog, 'folder:missing')).toBeNull() + expect(getExecutionHostIdForWorktree(emptyCatalog, 'folder:missing')).toBe('local') + }) + + it.each([ + ['an ownerless repo row', { repos: [{ id: 'r' }] }, 'r::wt', 'local'], + ['an SSH repo row', { repos: [{ id: 'r', connectionId: 'box' }] }, 'r::wt', 'ssh:box'], + [ + 'a per-worktree host', + { worktreesByRepo: { r: [{ id: 'r::wt', repoId: 'r', hostId: 'ssh:box' }] } }, + 'r::wt', + 'ssh:box' + ], + [ + 'a folder-workspace row', + { folderWorkspaces: [{ id: 'f', projectGroupId: 'g' }] }, + 'folder:f', + 'local' + ], + ['the floating workspace', {}, FLOATING_TERMINAL_WORKTREE_ID, 'local'] + ] as const)('answers positively for %s', (_label, catalog, worktreeId, expected) => { + expect(getKnownExecutionHostIdForWorktree(catalog, worktreeId)).toBe(expected) + expect(getExecutionHostIdForWorktree(catalog, worktreeId)).toBe(expected) + }) +}) diff --git a/src/renderer/src/lib/worktree-runtime-owner.ts b/src/renderer/src/lib/worktree-runtime-owner.ts index 473b306eeaf..091cf246a9f 100644 --- a/src/renderer/src/lib/worktree-runtime-owner.ts +++ b/src/renderer/src/lib/worktree-runtime-owner.ts @@ -14,6 +14,7 @@ import { } from './worktree-runtime-owner-index' import { getSingleFocusedRuntimeEnvironmentId } from './single-runtime-legacy-owner' import { + findFolderWorkspaceOwner, getExecutionHostIdForFolderWorkspace, getExplicitRuntimeEnvironmentIdForFolderWorkspace, getRuntimeEnvironmentIdForFolderWorkspace @@ -157,10 +158,26 @@ export function getExplicitRuntimeEnvironmentIdForWorktree( return getExplicitRuntimeEnvironmentIdFromHost(getRepoExecutionHostId(repo)) } -export function getExecutionHostIdForWorktree( +function getFocusedRuntimeOrLocalExecutionHostId( + state: WorktreeRuntimeOwnerState +): ExecutionHostId { + const environmentId = getSingleFocusedRuntimeEnvironmentId(state) + return environmentId ? `runtime:${encodeURIComponent(environmentId)}` : 'local' +} + +/** + * The catalog's answer, or `null` when it has none: no row names an owner for this worktree (a git + * worktree without a repo row, a folder workspace without a folder-workspace row) and nothing more + * specific — active-workspace host, detected owner, per-worktree host — applies either. A row that + * exists and names no owner is a positive `'local'`; a row that has not landed is silence. + * {@link getExecutionHostIdForWorktree} papers over that silence with the focused-runtime-or-local + * default, which is the right answer for routing an operation and the wrong one for a caller that + * reads the host as evidence. + */ +export function getKnownExecutionHostIdForWorktree( state: WorktreeRuntimeOwnerState, worktreeId: string | null | undefined -): ExecutionHostId { +): ExecutionHostId | null { if (!worktreeId) { return 'local' } @@ -173,7 +190,11 @@ export function getExecutionHostIdForWorktree( } const workspaceScope = parseWorkspaceKey(worktreeId) if (workspaceScope?.type === 'folder') { - return getExecutionHostIdForFolderWorkspace(state, workspaceScope.folderWorkspaceId) + const hostId = getExecutionHostIdForFolderWorkspace(state, workspaceScope.folderWorkspaceId) + // Why: the folder resolver substitutes `'local'` for a missing row the same way this one does. + return hostId === 'local' && !findFolderWorkspaceOwner(state, workspaceScope.folderWorkspaceId) + ? null + : hostId } const hasDetectedOwner = hasIndexedDetectedWorktree(state.detectedWorktreesByRepo, worktreeId) if (hasDetectedOwner) { @@ -196,12 +217,24 @@ export function getExecutionHostIdForWorktree( } const repoId = worktree?.repoId ?? getRepoIdFromWorktreeId(worktreeId) const repo = findRepoRecord(state.repos, repoId) - const hasExplicitOwner = Boolean(repo?.executionHostId?.trim() || repo?.connectionId?.trim()) - if (repo && hasExplicitOwner) { + if (!repo) { + return null + } + const hasExplicitOwner = Boolean(repo.executionHostId?.trim() || repo.connectionId?.trim()) + if (hasExplicitOwner) { return getRepoExecutionHostId(repo) } - const environmentId = getSingleFocusedRuntimeEnvironmentId(state) - return environmentId ? `runtime:${encodeURIComponent(environmentId)}` : 'local' + return getFocusedRuntimeOrLocalExecutionHostId(state) +} + +export function getExecutionHostIdForWorktree( + state: WorktreeRuntimeOwnerState, + worktreeId: string | null | undefined +): ExecutionHostId { + return ( + getKnownExecutionHostIdForWorktree(state, worktreeId) ?? + getFocusedRuntimeOrLocalExecutionHostId(state) + ) } export function getSettingsForWorktreeRuntimeOwner( diff --git a/tests/e2e/ssh-stale-resume-execution-host-scope.spec.ts b/tests/e2e/ssh-stale-resume-execution-host-scope.spec.ts new file mode 100644 index 00000000000..03e72adfd98 --- /dev/null +++ b/tests/e2e/ssh-stale-resume-execution-host-scope.spec.ts @@ -0,0 +1,306 @@ +/** + * A provider session id names a transcript in ONE machine's agent state directory. Orca issued one + * against the wrong machine and the agent answered + * `No conversation found with session ID: ` in the user's remote terminal. + * + * Nothing in the resume path was host-scoped. `worktreeId` is `repoId::path` with no host component, + * sleeping records merge across every host partition at boot without retaining which one they came + * from, and the launch path resolves its target from the *current* catalog — so a record captured on + * host A reaches a `--resume` executed on host B. + * + * This lane proves it at the only altitude that settles the question: the argv that actually lands + * on the remote machine. Both tests restart the app across a relay kill (the shape of an Orca + * update, which is what the user did) and read the stub agent's argv ledger out of the container. + * + * - foreign stamp → the ledger must hold no `--resume`, and the record must survive so the user + * can still resume by hand. It must still hold Orca's ordinary `--version` + * probe, or the lane would pass on an app that never reached the host at all. + * - matching stamp → the ledger must contain `--resume `. + * + * The second is not a nicety. Without it the first passes on any app that resumes nothing at all, + * which is exactly the failure mode a refuse-everything gate would ship. + */ +import type { ElectronApplication, TestInfo } from '@stablyai/playwright-test' +import { test, expect } from './helpers/orca-app' +import { createRestartSession } from './helpers/orca-restart' +import { connectDockerSshRelayTarget } from './helpers/docker-ssh-relay-connection' +import { killDockerSshRelayDaemon } from './helpers/docker-ssh-relay-faults' +import { + cleanupDockerSshRelayTarget, + execDockerSshRelayTargetCommand, + startDockerSshRelayTarget, + writeDockerSshRelayTargetFile, + type DockerSshRelayTarget +} from './helpers/docker-ssh-relay-target' +import { waitForActiveWorktree, waitForSessionReady } from './helpers/store' +import { + waitForActivePaneHookDescriptor, + waitForActivePanePtyId, + waitForActiveTerminalManager +} from './helpers/terminal' + +const RUN_DOCKER_SSH = process.env.ORCA_E2E_SSH_DOCKER === '1' + +const SESSION_ID = 'e2e-stale-resume-87987465' +const ARGV_LEDGER = '/tmp/orca-e2e-claude-argv.log' +/** Stands in for a record the user carried over from another machine: its transcript is not on this + * host under this id. Any value that is not the connected target's works. */ +const FOREIGN_CONNECTION_ID = 'orca-e2e-some-other-host' +/** Resolve the stamp to the connected target's own id, which is only minted during connect. */ +const STAMP_OWNING_HOST = Symbol('stamp-owning-host') +/** How long a `--resume` gets to reach the host once the relaunched pane holds its PTY. The resume + * is typed into that very shell, so anything the gate let through lands well inside this. */ +const RESUME_GRACE_MS = 20_000 + +test.use({ seedTestRepo: false }) + +/** A `claude` that records the argv it was invoked with and then holds the PTY open the way the real + * binary does. The ledger outlives the pane, and is appended to rather than truncated so a second + * invocation is visible as a second line. */ +function installRemoteClaudeArgvLedger(target: DockerSshRelayTarget): void { + writeDockerSshRelayTargetFile( + target, + '/usr/local/bin/claude', + [ + '#!/bin/sh', + `printf 'ARGV [%s] pid=%s ppid=%s %s\\n' "$(date +%s)" "$$" "$PPID" "$*" >> ${ARGV_LEDGER}`, + 'exec cat', + '' + ].join('\n') + ) + execDockerSshRelayTargetCommand(target, 'chmod 755 /usr/local/bin/claude') +} + +function readRemoteArgvLedger(target: DockerSshRelayTarget): string { + return execDockerSshRelayTargetCommand(target, `cat ${ARGV_LEDGER} 2>/dev/null || true`).trim() +} + +/** The lines appended since `baseline`. The ledger is append-only and the first launch already wrote + * its own `--version` probe to it, so "non-empty" says nothing about the relaunch — only the tail + * beyond what was there at quit does. Reading the whole ledger here is exactly the race that let the + * control case read two `--version` lines and give up before the resume was typed. */ +function ledgerLinesSince(ledger: string, baseline: string): string { + return ledger.startsWith(baseline) ? ledger.slice(baseline.length).trim() : ledger +} + +/** Poll the relaunch's ledger lines until `until` holds or the budget runs out. Returns them either + * way: the negative case asserts on what did NOT arrive, so this must not throw. */ +async function settleRemoteArgvLedger( + target: DockerSshRelayTarget, + baseline: string, + budgetMs: number, + until: (fresh: string) => boolean +): Promise { + const deadline = Date.now() + budgetMs + for (;;) { + const fresh = ledgerLinesSince(readRemoteArgvLedger(target), baseline) + if (until(fresh) || Date.now() >= deadline) { + return fresh + } + await new Promise((resolve) => setTimeout(resolve, 2_000)) + } +} + +/** + * One full incident replay: capture a sleeping agent record on the SSH worktree stamped with + * `stamp`, quit, kill the relay so no PTY can be reclaimed (without that the pane's live PTY + * suppresses the resume and the test proves nothing), relaunch, and report what reached the remote. + */ +async function resumeAcrossRestart( + testInfo: TestInfo, + target: DockerSshRelayTarget, + stamp: string | typeof STAMP_OWNING_HOST, + resumeBudgetMs: number +): Promise<{ + ledger: string + recordSurvived: boolean + diagnostics: { + recordStamp: string + entryStamp: string + ledgerBeforeQuit: string + ledgerAfterQuit: string + } +}> { + const restart = createRestartSession(testInfo) + let firstApp: ElectronApplication | null = null + let secondApp: ElectronApplication | null = null + try { + const firstLaunch = await restart.launch() + firstApp = firstLaunch.app + await waitForSessionReady(firstLaunch.page) + const remote = await connectDockerSshRelayTarget(firstLaunch.page, target) + await expect + .poll(() => waitForActiveWorktree(firstLaunch.page), { timeout: 60_000 }) + .toBe(remote.worktreeId) + await waitForActiveTerminalManager(firstLaunch.page, 60_000) + const descriptor = await waitForActivePaneHookDescriptor(firstLaunch.page, 60_000) + + // Why seeded rather than driven by a real agent: a real `claude` run needs an install and auth + // in the container. This is the same store entry the hook server writes, so the capture, + // persistence and resume paths under test are the production ones. + await firstLaunch.page.evaluate( + ({ paneKey, worktreeId, providerSessionId, connectionId }) => { + window.__store?.getState().setAgentStatus( + paneKey, + { state: 'working', prompt: 'finish the task', agentType: 'claude' }, + 'Claude', + undefined, + { worktreeId, connectionId }, + { + providerSession: { key: 'session_id', id: providerSessionId }, + launchConfig: { agentCommand: 'claude', agentArgs: '', agentEnv: {} } + } + ) + }, + { + paneKey: descriptor.paneKey, + worktreeId: remote.worktreeId, + providerSessionId: SESSION_ID, + connectionId: stamp === STAMP_OWNING_HOST ? remote.targetId : stamp + } + ) + + await firstLaunch.page.evaluate(() => window.dispatchEvent(new Event('beforeunload'))) + await expect + .poll( + () => + firstLaunch.page.evaluate( + async ({ targetId, sessionId }) => { + // The SSH worktree's rows live in the `ssh:` partition, globals in `local`. + const [local, host] = await Promise.all([ + window.api.session.get(), + window.api.session.get(`ssh:${targetId}`) + ]) + return [ + ...Object.values(local.sleepingAgentSessionsByPaneKey ?? {}), + ...Object.values(host.sleepingAgentSessionsByPaneKey ?? {}) + ].some((record) => record.providerSession.id === sessionId) + }, + { targetId: remote.targetId, sessionId: SESSION_ID } + ), + { timeout: 30_000, message: 'the sleeping agent record was never persisted before quit' } + ) + .toBe(true) + + const ledgerBeforeQuit = readRemoteArgvLedger(target) + + await restart.close(firstApp) + firstApp = null + // The shape of an Orca update: the relay and every PTY under it are gone, so nothing is + // reclaimable and the sleeping record is the only way the agent comes back. + killDockerSshRelayDaemon(target) + const ledgerAfterQuit = readRemoteArgvLedger(target) + + const secondLaunch = await restart.launch() + secondApp = secondLaunch.app + await waitForSessionReady(secondLaunch.page, 60_000) + await expect + .poll(() => waitForActiveWorktree(secondLaunch.page), { timeout: 90_000 }) + .toBe(remote.worktreeId) + await waitForActiveTerminalManager(secondLaunch.page, 90_000) + // The cold-restore decision is made before the replacement PTY is spawned, so a bound PTY + // means the gate has already ruled on this record — after this, waiting is only for the + // typed command to travel. + await waitForActivePanePtyId(secondLaunch.page, 90_000) + // Orca's per-launch `claude --version` probe proves the relaunch reached the host at all; the + // negative case is vacuous without it. + await settleRemoteArgvLedger(target, ledgerAfterQuit, 90_000, (fresh) => + fresh.includes('--version') + ) + const ledger = await settleRemoteArgvLedger(target, ledgerAfterQuit, resumeBudgetMs, (fresh) => + fresh.includes('--resume') + ) + // Why this is reported rather than merely asserted: the two host stamps are what the gate reads, + // so a failure that does not name them cannot be told apart from the gate simply not running. + const diagnostics = await secondLaunch.page.evaluate((sessionId) => { + const state = window.__store?.getState() + const record = Object.values(state?.sleepingAgentSessionsByPaneKey ?? {}).find( + (candidate) => candidate.providerSession.id === sessionId + ) + const entry = Object.values(state?.agentStatusByPaneKey ?? {}).find( + (candidate) => candidate.providerSession?.id === sessionId + ) + return { + recordStamp: record ? String(record.connectionId) : 'no-record', + entryStamp: entry ? String(entry.connectionId) : 'no-entry' + } + }, SESSION_ID) + return { + ledger, + recordSurvived: diagnostics.recordStamp !== 'no-record', + diagnostics: { ...diagnostics, ledgerBeforeQuit, ledgerAfterQuit } + } + } finally { + if (secondApp) { + await restart.close(secondApp) + } + if (firstApp) { + await restart.close(firstApp) + } + await restart.dispose() + } +} + +test.describe('SSH sleeping-agent resume execution-host scope', () => { + test.skip(!RUN_DOCKER_SSH, 'Set ORCA_E2E_SSH_DOCKER=1 to run Docker-backed SSH tests.') + test.skip(process.platform === 'win32', 'Docker SSH tests use POSIX ssh tooling.') + test.describe.configure({ mode: 'serial' }) + + test("does not issue another host's session id against the SSH host", async (// oxlint-disable-next-line no-empty-pattern -- This restart test owns both Electron launches. + {}, testInfo) => { + test.setTimeout(600_000) + let target: DockerSshRelayTarget | null = null + try { + target = startDockerSshRelayTarget(testInfo) + installRemoteClaudeArgvLedger(target) + + const result = await resumeAcrossRestart( + testInfo, + target, + FOREIGN_CONNECTION_ID, + RESUME_GRACE_MS + ) + + // Why not an empty ledger: Orca legitimately probes `claude --version` on the remote to + // detect installed agents, once per launch. That is not a resume. The defect is `--resume` + // carrying an id this machine never wrote, so that is what must be absent. `result.ledger` + // is only what the relaunch appended, so the first launch's probe cannot satisfy this. + expect( + result.ledger, + `Orca ran the agent on the SSH host with a session id captured on another machine.\nrecord stamp: ${result.diagnostics.recordStamp}\nlive entry stamp: ${result.diagnostics.entryStamp}\nledger before quit: ${JSON.stringify(result.diagnostics.ledgerBeforeQuit)}\nledger after quit+relay kill: ${JSON.stringify(result.diagnostics.ledgerAfterQuit)}` + ).not.toContain('--resume') + expect(result.ledger).not.toContain(SESSION_ID) + // The relaunch's lines must not be empty either, or this proves only that the agent never + // ran at all. + expect( + result.ledger, + 'the stub agent was never invoked by the relaunch, so the lane proves nothing' + ).toContain('--version') + // Declining is only recoverable if the record survives; deleting it on a host disagreement + // would destroy the user's only handle on that transcript. + expect(result.recordSurvived, 'the declined record was discarded, not preserved').toBe(true) + } finally { + cleanupDockerSshRelayTarget(target) + } + }) + + test('still resumes a session captured on the SSH host that owns the workspace', async (// oxlint-disable-next-line no-empty-pattern -- This restart test owns both Electron launches. + {}, testInfo) => { + test.setTimeout(600_000) + let target: DockerSshRelayTarget | null = null + try { + target = startDockerSshRelayTarget(testInfo) + installRemoteClaudeArgvLedger(target) + + // The control for the test above: the same machinery, one field different, and the resume + // must still land on the remote. + const result = await resumeAcrossRestart(testInfo, target, STAMP_OWNING_HOST, 90_000) + + expect(result.ledger, 'the legitimate resume never reached the SSH host').toContain( + `--resume ${SESSION_ID}` + ) + } finally { + cleanupDockerSshRelayTarget(target) + } + }) +}) From b7d694ff7ed85d6ab48df24ed0df9b1dd58091d2 Mon Sep 17 00:00:00 2001 From: Vincent <47273853+Tkotm76@users.noreply.github.com> Date: Fri, 18 Sep 2026 07:31:31 +0200 Subject: [PATCH 096/168] feat(composer): choose a base ref in the New Workspace composer (#17250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(repo): share the create-from picker outside automations Move CreateFromPicker and its test from components/automations to components/repo, next to the repo-scoped shared UI that already lives there (RepoCombobox, RepoBadgeLabel, repo-icon). The New Workspace composer will consume this picker instead of growing a second base-ref combobox. Pure move: no behavior change. The translate() keys are call-site literals, so no locale catalog is affected. Co-Authored-By: Claude Opus 5 (1M context) * fix(composer): separate the branch that names a workspace from its base baseBranch carried two meanings at once. It is the ref a worktree is created from, and it is also what buildWorkspaceSourceSelection turns into the name field's branch pill whenever no work item is linked. Any second control that set a base therefore took the name field over: the pill replaced the text input, hiding whatever the user had typed. The name survived in state, and Advanced still exposed it, but the main field silently stopped showing it. Add baseBranchNamesWorkspace, true only when a branch was picked to name the workspace. The pill reads that flag; creation keeps reading baseBranch. Two call sites set it, because those are the only paths that make baseBranch defined with nothing linked — and an undefined base yields no pill anyway. Co-Authored-By: Claude Opus 5 (1M context) * feat(composer): let the New Workspace composer pick its base ref The name field's tabs pick how a workspace is named; the base ref is a separate decision the composer never exposed. Naming a workspace from a Jira, Linear, GitHub or GitLab issue therefore pinned the project's default base with no way to start from a release or a long-lived feature branch. Nothing below the UI was missing. baseBranch already crosses IPC next to linkedWorkItem and wins over every default in main, and the composer already computed handleBaseBranchChange and startFromResetHint — the card simply never declared those props, so its {...props} spread dropped them. Declare them and render the shared create-from picker under the name field. ComposerBaseRefPicker owns its own store reads, the way the sibling ComposerParentWorktreePicker already does, so the name section stays presentational and nothing subscribes to the worktree list while the picker is hidden. The picker is offered for a plain typed name and for issue-shaped sources. It is hidden where a base already exists: PR/MR sources pin the pull request's own head, a branch pick IS the base — and offering one there would silently turn a checkout of that branch into a new branch off something else, since picking a base clears reuse — and folder workspaces have no branches. It always opens on the project default: no sticky base. Co-Authored-By: Claude Opus 5 (1M context) * chore(repo): drop a stale react-doctor suppression on the create-from picker no-adjust-state-on-prop-change no longer fires on this file: removing the directive and running the react-doctor pass over the directory — where the JS plugin actually loads — reports nothing, at the new path and at the old one on main alike. The suppression was already dead; the rename only put the file in the changed set, where the quality gate reports unused directives. Co-Authored-By: Claude Opus 5 (1M context) * feat(repo): list branches as soon as the create-from picker opens The picker only searched once two characters were typed, so opening it showed just the project default and whatever branches already had a worktree. The composer's Branch tab lists on an empty query through the same runtime helper; match it, and the picker offers the repo's branches straight away. Search stays debounced at 200ms and capped at 30 results, and it still runs on the repo's own execution host, so a remote repo lists its own branches. The Automations picker shares this component and gains the same listing. Co-Authored-By: Claude Opus 5 (1M context) * fix(composer): carry the base-ref naming intent through a saved draft `baseBranchNamesWorkspace` lived only in component state, so restoring a persisted draft always reset it to true. A base ref chosen in the picker came back as a name-field source pill, hiding the name the user had typed — the exact regression the flag exists to prevent, reappearing across a draft round trip. Persist it next to `baseBranch` and restore it through `resolveDraftBaseBranchNamesWorkspace`. A draft written before the flag existed records no intent and restores as a branch pick, which is the behavior it had when it was saved. Co-Authored-By: Claude Opus 5 (1M context) * fix(composer): preserve independent base and branch name choices * fix(composer): pass naming-intent through the create-more reset test IssueSourceActions now requires baseBranchNamesWorkspace. The create-more reset fixture is a source-owned base, so the flag stays true and the next create still clears it. --------- Co-authored-by: Claude Opus 5 (1M context) Co-authored-by: Neil --- ...wWorkspaceComposerCard.start-from.test.tsx | 229 ++++++++++++++++++ .../automations/AutomationWorkspaceField.tsx | 2 +- .../new-workspace/ComposerBaseRefPicker.tsx | 42 ++++ .../NewWorkspaceComposerNameSection.tsx | 25 +- .../base-ref-picker-visibility.test.ts | 44 ++++ .../base-ref-picker-visibility.ts | 25 ++ .../new-workspace-composer-card-props.ts | 3 + .../CreateFromPicker.test.tsx | 36 ++- .../CreateFromPicker.tsx | 5 +- .../branch-start-point-actions.test.ts | 95 ++++++++ .../branch-start-point-actions.ts | 24 +- .../composer-state/composer-external-sync.ts | 1 + .../composer-name-source-selection.test.ts | 193 +++++++++++++++ .../composer-state/composer-source-state.ts | 6 + .../composer-state/composer-target-state.ts | 1 + .../composer-state/draft-target-sync.test.ts | 20 ++ .../hooks/composer-state/draft-target-sync.ts | 5 +- .../github-provider-selection.ts | 7 +- .../github-submit-resolution.ts | 5 + .../hooks/composer-state/identity-model.ts | 2 + .../composer-state/issue-source-actions.ts | 18 +- .../multiple-create-reset.test.ts | 2 + .../work-item-source-actions.ts | 4 + .../workspace-identity-state.ts | 23 ++ .../store/slices/ui/ui-slice-contract-core.ts | 3 + 25 files changed, 805 insertions(+), 15 deletions(-) create mode 100644 src/renderer/src/components/NewWorkspaceComposerCard.start-from.test.tsx create mode 100644 src/renderer/src/components/new-workspace/ComposerBaseRefPicker.tsx create mode 100644 src/renderer/src/components/new-workspace/base-ref-picker-visibility.test.ts create mode 100644 src/renderer/src/components/new-workspace/base-ref-picker-visibility.ts rename src/renderer/src/components/{automations => repo}/CreateFromPicker.test.tsx (74%) rename src/renderer/src/components/{automations => repo}/CreateFromPicker.tsx (97%) create mode 100644 src/renderer/src/hooks/composer-state/branch-start-point-actions.test.ts create mode 100644 src/renderer/src/hooks/composer-state/composer-name-source-selection.test.ts diff --git a/src/renderer/src/components/NewWorkspaceComposerCard.start-from.test.tsx b/src/renderer/src/components/NewWorkspaceComposerCard.start-from.test.tsx new file mode 100644 index 00000000000..5b186fc9378 --- /dev/null +++ b/src/renderer/src/components/NewWorkspaceComposerCard.start-from.test.tsx @@ -0,0 +1,229 @@ +// @vitest-environment happy-dom + +import React, { act } from 'react' +import { createRoot } from 'react-dom/client' +import { afterEach, describe, expect, it, vi } from 'vitest' +import NewWorkspaceComposerCard from './NewWorkspaceComposerCard' + +vi.mock('@/store', () => ({ + useAppStore: Object.assign( + (selector: (state: unknown) => unknown) => + selector({ + closeModal: vi.fn(), + openModal: vi.fn(), + openSettingsPage: vi.fn(), + openSettingsTarget: vi.fn(), + setRuntimeEnvironmentStatus: vi.fn(), + activeModal: 'new-workspace-composer', + settings: { defaultTuiAgent: null, disabledTuiAgents: [] }, + updateSettings: vi.fn(), + projects: [], + repos: [], + worktreesByRepo: {} + }), + { getState: () => ({}) } + ) +})) + +vi.mock('@/components/contextual-tours/use-contextual-tour', () => ({ + useContextualTour: vi.fn() +})) + +vi.mock('@/components/ui/tooltip', () => ({ + Tooltip: ({ children }: { children: React.ReactNode }) => <>{children}, + TooltipContent: ({ children }: { children: React.ReactNode }) => <>{children}, + TooltipTrigger: ({ children }: { children: React.ReactNode }) => <>{children} +})) + +vi.mock('@/components/agent/AgentCombobox', () => ({ + default: () => +})) + +vi.mock('@/components/sidebar/AddRemoteHostDialog', () => ({ + AddRemoteHostDialog: () => null +})) + +vi.mock('@/components/new-workspace/SmartWorkspaceNameField', () => ({ + default: () => +})) + +vi.mock('@/components/new-workspace/ProjectCombobox', () => ({ + default: () =>
+})) + +// Why: the picker owns its own test; here it only has to report its value and emit picks. +vi.mock('@/components/repo/CreateFromPicker', () => ({ + CreateFromPicker: ({ + value, + onValueChange + }: { + value: string + onValueChange: (next: string) => void + }) => ( +
+ + +
+ ) +})) + +function renderCard( + overrides: Partial> = {} +): HTMLDivElement { + const container = document.createElement('div') + document.body.appendChild(container) + act(() => { + createRoot(container).render( + {}} + eligibleRepos={[]} + repoId="repo-a" + selectedRepoIsGit + onRepoChange={() => {}} + onProjectChange={() => {}} + primaryActionLabel="Create workspace" + name="" + onNameValueChange={() => {}} + branchNameOverride={undefined} + onBranchNameOverrideChange={() => {}} + onSmartGitHubItemSelect={() => {}} + onSmartGitLabItemSelect={() => {}} + onSmartBranchSelect={() => {}} + onSmartLinearIssueSelect={() => {}} + smartNameSelection={{ kind: 'jira', label: 'ERP-1491' }} + onClearSmartNameSelection={() => {}} + canReuseSelectedBranch={false} + reuseSelectedBranch={false} + onReuseSelectedBranchChange={() => {}} + forkPushWarning={null} + detectedAgentIds={null} + onOpenAgentSettings={() => {}} + advancedOpen={false} + onToggleAdvanced={() => {}} + parentWorktreeId={null} + onParentWorktreeIdChange={() => {}} + createDisabled={false} + projectError={null} + creating={false} + onCreate={() => {}} + note="" + onNoteChange={() => {}} + setupConfig={null} + requiresExplicitSetupChoice={false} + setupDecision={null} + onSetupDecisionChange={() => {}} + setupAgentStartupPolicy="start-immediately" + onSetupAgentStartupPolicyChange={() => {}} + shouldWaitForSetupCheck={false} + resolvedSetupDecision={null} + createError={null} + selectedRepoConnectionId={null} + selectedRepoSshStatus={null} + selectedRepoRequiresConnection={false} + selectedRepoConnectInProgress={false} + onConnectSelectedRepo={async () => {}} + canUseSparseCheckout={false} + sparsePresets={[]} + sparseSelectedPresetId={null} + onSparseSelectPreset={() => {}} + branchesEnabled + setupControlsEnabled={false} + sparseControlsEnabled={false} + baseBranch={undefined} + onBaseBranchChange={() => {}} + startFromResetHint={null} + {...overrides} + /> + ) + }) + return container +} + +function clickButton(container: HTMLDivElement, label: string): void { + const button = [...container.querySelectorAll('button')].find( + (candidate) => candidate.textContent === label + ) + act(() => button?.click()) +} + +describe('NewWorkspaceComposerCard start from', () => { + let container: HTMLDivElement | null = null + + afterEach(() => { + container?.remove() + container = null + }) + + it('offers a base ref while a Jira issue names the workspace', () => { + container = renderCard() + + expect(container.querySelector('[data-testid="base-ref-picker"]')).toBeTruthy() + }) + + it('reports the picked ref to the composer', () => { + const picks: (string | undefined)[] = [] + container = renderCard({ onBaseBranchChange: (next) => picks.push(next) }) + + clickButton(container, 'Pick release') + + expect(picks).toEqual(['release/1.2']) + }) + + it('reports the project default as no base at all', () => { + const picks: (string | undefined)[] = [] + container = renderCard({ + baseBranch: 'release/1.2', + onBaseBranchChange: (next) => picks.push(next) + }) + + clickButton(container, 'Pick project default') + + expect(picks).toEqual([undefined]) + }) + + // Why: picking a base clears reuse, so offering one here would silently turn a checkout of + // the picked branch into a new branch off something else. + it('omits the base ref for a branch source, which already is the base', () => { + container = renderCard({ + smartNameSelection: { kind: 'branch', label: 'feature/export-v2' }, + baseBranch: 'feature/export-v2' + }) + + expect(container.querySelector('[data-testid="base-ref-picker"]')).toBeNull() + }) + + it('offers the base ref while a plain typed name owns the field', () => { + container = renderCard({ smartNameSelection: null, name: 'my-own-name' }) + + expect(container.querySelector('[data-testid="base-ref-picker"]')).toBeTruthy() + }) + + it.each([ + ['github-pr', { kind: 'github-pr' as const, label: '#42 Fix' }], + ['gitlab-mr', { kind: 'gitlab-mr' as const, label: '!42 Fix' }] + ])( + 'omits the base ref for a %s source that carries its own base', + (_label, smartNameSelection) => { + container = renderCard({ smartNameSelection }) + + expect(container.querySelector('[data-testid="base-ref-picker"]')).toBeNull() + } + ) + + it('omits the base ref when branches are disabled', () => { + container = renderCard({ branchesEnabled: false }) + + expect(container.querySelector('[data-testid="base-ref-picker"]')).toBeNull() + }) + + it('surfaces the reset hint left by a project switch', () => { + container = renderCard({ startFromResetHint: 'was origin/main' }) + + expect(container.textContent).toContain('was origin/main') + }) +}) diff --git a/src/renderer/src/components/automations/AutomationWorkspaceField.tsx b/src/renderer/src/components/automations/AutomationWorkspaceField.tsx index 80e6feeda32..9bbcf37cb9a 100644 --- a/src/renderer/src/components/automations/AutomationWorkspaceField.tsx +++ b/src/renderer/src/components/automations/AutomationWorkspaceField.tsx @@ -1,12 +1,12 @@ import { Info } from 'lucide-react' import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { CreateFromPicker } from '@/components/repo/CreateFromPicker' import { translate } from '@/i18n/i18n' import type { AutomationWorkspaceMode } from '../../../../shared/automations-types' import type { Repo } from '../../../../shared/repo-types' import type { Worktree } from '../../../../shared/worktree/types' import { AUTOMATION_EDITOR_SECTION_LABEL_CLASS, Field } from './automation-page-parts' -import { CreateFromPicker } from './CreateFromPicker' import { WorkspaceCombobox } from './WorkspaceCombobox' import type { AutomationDraft } from './AutomationEditorDialog' diff --git a/src/renderer/src/components/new-workspace/ComposerBaseRefPicker.tsx b/src/renderer/src/components/new-workspace/ComposerBaseRefPicker.tsx new file mode 100644 index 00000000000..e97f980ee87 --- /dev/null +++ b/src/renderer/src/components/new-workspace/ComposerBaseRefPicker.tsx @@ -0,0 +1,42 @@ +import React from 'react' +import { CreateFromPicker } from '@/components/repo/CreateFromPicker' +import { useRepoMap, useWorktreesForRepo } from '@/store/selectors' + +type ComposerBaseRefPickerProps = { + repoId: string + baseBranch: string | undefined + onBaseBranchChange: (value: string | undefined) => void + resetHint: string | null | undefined +} + +/** + * Base ref control for the New Workspace composer. + * + * Owns its own store reads so the name section stays presentational and the + * worktree subscription only exists while the picker is actually on screen. + */ +export function ComposerBaseRefPicker({ + repoId, + baseBranch, + onBaseBranchChange, + resetHint +}: ComposerBaseRefPickerProps): React.JSX.Element { + const repoMap = useRepoMap() + const repoWorktrees = useWorktreesForRepo(repoId) + return ( +
+ onBaseBranchChange(nextBaseBranch || undefined)} + /> + {resetHint ?

{resetHint}

: null} +
+ ) +} + +export default ComposerBaseRefPicker diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.tsx b/src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.tsx index 225e0f790a5..f05ef9d06a1 100644 --- a/src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.tsx +++ b/src/renderer/src/components/new-workspace/NewWorkspaceComposerNameSection.tsx @@ -3,6 +3,8 @@ import { AlertTriangle, Check } from 'lucide-react' import SmartWorkspaceNameField from '@/components/new-workspace/SmartWorkspaceNameField' import { cn } from '@/lib/utils' import { translate } from '@/i18n/i18n' +import { shouldShowComposerBaseRefPicker } from './base-ref-picker-visibility' +import { ComposerBaseRefPicker } from './ComposerBaseRefPicker' import type { NewWorkspaceComposerCardProps } from './new-workspace-composer-card-props' type NewWorkspaceComposerNameSectionProps = Pick< @@ -35,6 +37,9 @@ type NewWorkspaceComposerNameSectionProps = Pick< | 'canReuseSelectedBranch' | 'reuseSelectedBranch' | 'onReuseSelectedBranchChange' + | 'baseBranch' + | 'onBaseBranchChange' + | 'startFromResetHint' > & { onNamePlainEnter: () => void } @@ -68,8 +73,18 @@ export function NewWorkspaceComposerNameSection({ forkPushWarning, canReuseSelectedBranch, reuseSelectedBranch, - onReuseSelectedBranchChange + onReuseSelectedBranchChange, + baseBranch, + onBaseBranchChange, + startFromResetHint }: NewWorkspaceComposerNameSectionProps): React.JSX.Element { + const showBaseRefPicker = + Boolean(onBaseBranchChange) && + shouldShowComposerBaseRefPicker({ + selectedRepoIsGit, + branchesEnabled, + smartNameSelectionKind: smartNameSelection?.kind ?? null + }) return (
{remoteAccountScopeNotice} + {/* Why not in a remote scope: the link belongs to a login running on + this desktop, which has nothing to do with the server named above. */} + {isRemoteAccountScope ? null : }