Unified · Split
| @@ -124,7 +124,7 @@ function AutoProjectBootstrap() { · 123 unmodified lines | ||
| 124 | 124 | |
| 125 | 125 | function DesktopProjectBootstrap() { |
| 126 | 126 | const api = useNativeApi(); |
| 127 | − const { dispatch } = useStore(); | |
| 127 | + const { state, dispatch } = useStore(); | |
| 128 | 128 | const bootstrappedRef = useRef(false); |
| 129 | 129 | |
| 130 | 130 | useEffect(() => { |
| @@ -138,6 +138,16 @@ function DesktopProjectBootstrap() { · 7 unmodified lines | ||
| 138 | 138 | try { |
| 139 | 139 | const projects = await api.projects.list(); |
| 140 | 140 | if (disposed) return; |
| 141 | + if (projects.length === 0 && (state.projects.length > 0 || state.threads.length > 0)) { | |
| 142 | + // Avoid wiping hydrated renderer state if the initial projects.list races | |
| 143 | + // backend startup and transiently returns no projects. | |
| 144 | + retryTimer = setTimeout(() => { | |
| 145 | + retryTimer = null; | |
| 146 | + void attemptBootstrap(); | |
| 147 | + }, retryDelayMs); | |
| 148 | + retryDelayMs = Math.min(retryDelayMs * 2, 5_000); | |
| 149 | + return; | |
| 150 | + } | |
| 141 | 151 | dispatch({ |
| 142 | 152 | type: "SYNC_PROJECTS", |
| 143 | 153 | projects: projects.map((project) => ({ |
| @@ -167,7 +177,7 @@ function DesktopProjectBootstrap() { · 23 unmodified lines | ||
| 167 | 177 | clearTimeout(retryTimer); |
| 168 | 178 | } |
| 169 | 179 | }; |
| 170 | − }, [api, dispatch]); | |
| 180 | + }, [api, dispatch, state.projects.length, state.threads.length]); | |
| 171 | 181 | |
| 172 | 182 | return null; |
| 173 | 183 | } |
| @@ -668,6 +668,29 @@ async function main() { · 667 unmodified lines | ||
| 668 | 668 | ); |
| 669 | 669 | } |
| 670 | 670 | |
| 671 | + const interactionChecks = []; | |
| 672 | + const threadClicks = Number(donePayload.interactions?.threadClicks ?? 0); | |
| 673 | + const typedChars = Number(donePayload.interactions?.typedChars ?? 0); | |
| 674 | + const terminalOpened = donePayload.interactions?.terminal?.openedByShortcut === true; | |
| 675 | + const terminalCommandTouched = donePayload.interactions?.terminal?.commandFileTouched === true; | |
| 676 | + if (threadClicks <= 0) { | |
| 677 | + interactionChecks.push("thread clicks"); | |
| 678 | + } | |
| 679 | + if (typedChars <= 0) { | |
| 680 | + interactionChecks.push("typed chars"); | |
| 681 | + } | |
| 682 | + if (!terminalOpened) { | |
| 683 | + interactionChecks.push("terminal shortcut open"); | |
| 684 | + } | |
| 685 | + if (!terminalCommandTouched) { | |
| 686 | + interactionChecks.push("terminal command execution"); | |
| 687 | + } | |
| 688 | + if (interactionChecks.length > 0) { | |
| 689 | + throw new Error( | |
| 690 | + `Desktop perf automation completed without required scripted interactions: ${interactionChecks.join(", ")}`, | |
| 691 | + ); | |
| 692 | + } | |
| 693 | + | |
| 671 | 694 | const summary = summarizeTrace(tracePath); |
| 672 | 695 | const markdown = createMarkdownSummary({ |
| 673 | 696 | tracePath, |