[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

View File

@@ -0,0 +1,464 @@
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { runInNewContext } from "node:vm";
import { resolveRestoreCwdIntent } from "../../domain/sessionRestore.ts";
import {
getInitialTerminalStatus,
resolveTerminalVaultInitialized,
shouldResetConnectAutomationOnReconnect,
shouldSuppressHostStartupCommandOnReconnect,
shouldStartTerminalBackend,
} from "./restoredSessionGate.ts";
import { setVaultInitialized } from "../../application/state/vaultInitStore.ts";
test("Terminal restore preparation honors fresh login while preserving ordinary and local cwd restore", () => {
const source = readFileSync(new URL("../Terminal.tsx", import.meta.url), "utf8");
const start = source.indexOf("const prepareRestoredReconnect = useCallback");
const end = source.indexOf("// Set only once the inherited", start);
assert.ok(start >= 0 && end > start);
for (const protocol of ["ssh", undefined, "local"] as const) {
for (const requireFreshConnection of [false, true]) {
const restoreCwdIntentRef: { current: { command: string } | null } = { current: null };
runInNewContext(`${source.slice(start, end)}\nprepareRestoredReconnect();`, {
useCallback: (fn: () => void) => fn,
shouldSuppressHostStartupCommandOnReconnect,
resolveRestoreCwdIntent,
suppressHostStartupCommandRef: { current: false },
restoreCwdIntentRef,
restoreState: "restored-disconnected",
restoreTerminalCwd: true,
host: { protocol },
shellType: "posix",
lastCwd: "/srv/old-target",
isNetworkDevice: false,
requireFreshConnection,
});
if (requireFreshConnection && protocol !== "local") assert.equal(restoreCwdIntentRef.current, null);
else assert.equal(restoreCwdIntentRef.current?.command, "cd -- '/srv/old-target'");
}
}
});
test("restored disconnected sessions initialize as connecting", () => {
assert.equal(
getInitialTerminalStatus(),
"connecting",
);
});
test("normal sessions initialize as connecting", () => {
assert.equal(getInitialTerminalStatus(), "connecting");
});
test("restored disconnected sessions start terminal backend only after vault hydration", () => {
setVaultInitialized(false);
assert.equal(shouldStartTerminalBackend(), false);
setVaultInitialized(true);
assert.equal(shouldStartTerminalBackend(), true);
});
test("terminal boot waits for vaultInitialized before creating a backend session", () => {
const source = readFileSync(new URL("./useTerminalEffects.ts", import.meta.url), "utf8");
assert.match(
source,
/if \(!attachExistingSession && !vaultInitialized\) \{\s*\n\s*return;/,
"boot effect must wait for vault hydration before creating xterm/backend",
);
assert.match(
source,
/vaultInitialized,/,
"vaultInitialized must be in the boot effect dependency list",
);
});
test("terminal popup can supply its own completed vault hydration state", () => {
const terminalSource = readFileSync(new URL("../Terminal.tsx", import.meta.url), "utf8");
const popupSource = readFileSync(new URL("../TerminalPopupPage.tsx", import.meta.url), "utf8");
assert.match(
terminalSource,
/const vaultInitialized = resolveTerminalVaultInitialized\(\s*sharedVaultInitialized,\s*vaultInitializedOverride,\s*\);/,
);
assert.match(popupSource, /vaultInitializedOverride=\{vaultInitialized\}/);
assert.equal(resolveTerminalVaultInitialized(false, true), true);
assert.equal(resolveTerminalVaultInitialized(false), false);
assert.equal(resolveTerminalVaultInitialized(true, false), false);
});
test("host startup command policy distinguishes restored and automatic reconnects", () => {
assert.equal(shouldSuppressHostStartupCommandOnReconnect("restored"), false);
assert.equal(shouldSuppressHostStartupCommandOnReconnect("manual"), false);
assert.equal(shouldSuppressHostStartupCommandOnReconnect("automatic"), true);
});
test("connect automation reset policy distinguishes manual and automatic reconnects", () => {
assert.equal(shouldResetConnectAutomationOnReconnect("manual"), true);
assert.equal(shouldResetConnectAutomationOnReconnect("restored"), true);
assert.equal(shouldResetConnectAutomationOnReconnect("automatic"), false);
});
test("manual reconnect resets connect automation before opening a new session", () => {
const source = readFileSync(new URL("../Terminal.tsx", import.meta.url), "utf8");
const reconnectIndex = source.indexOf("const startReconnect = ");
const cancelBatchIndex = source.indexOf(
"await cancelConnectAutomationBatch(connectAutomationBatch)",
reconnectIndex,
);
const manualBranchIndex = source.indexOf(
'if (mode === "manual") {\n clearAutoReconnect',
cancelBatchIndex,
);
const stopFailureRetryIndex = source.indexOf(
'if (mode === "auto" && retryTokenStillCurrent())',
cancelBatchIndex,
);
const stopFailureDisconnectedIndex = source.indexOf(
'updateStatus("disconnected")',
stopFailureRetryIndex,
);
const resetConsumedIndex = source.indexOf(
"connectScriptsConsumedRef.current = false",
manualBranchIndex,
);
const resetCompletedIndex = source.indexOf(
"connectScriptsCompletedIdsRef.current = new Set()",
manualBranchIndex,
);
const resetInFlightIndex = source.indexOf(
"connectScriptsInFlightRef.current = false",
manualBranchIndex,
);
const connectingIndex = source.indexOf('updateStatus("connecting")', manualBranchIndex);
const autoElseIndex = source.indexOf("} else {", manualBranchIndex);
const autoSuppressIndex = source.indexOf(
'shouldSuppressHostStartupCommandOnReconnect("automatic")',
autoElseIndex,
);
assert.notEqual(reconnectIndex, -1);
assert.notEqual(cancelBatchIndex, -1);
assert.notEqual(manualBranchIndex, -1);
assert.notEqual(stopFailureRetryIndex, -1);
assert.notEqual(stopFailureDisconnectedIndex, -1);
assert.notEqual(resetConsumedIndex, -1);
assert.notEqual(resetCompletedIndex, -1);
assert.notEqual(resetInFlightIndex, -1);
assert.notEqual(connectingIndex, -1);
assert.notEqual(autoElseIndex, -1);
assert.notEqual(autoSuppressIndex, -1);
assert.ok(
cancelBatchIndex < manualBranchIndex
&& cancelBatchIndex < resetConsumedIndex
&& cancelBatchIndex < resetCompletedIndex
&& cancelBatchIndex < resetInFlightIndex,
"every reconnect must await onConnect cancellation before manual guards are cleared",
);
assert.ok(
stopFailureRetryIndex < stopFailureDisconnectedIndex
&& stopFailureDisconnectedIndex < manualBranchIndex,
"automatic reconnect must return to disconnected when stopping the old batch fails",
);
assert.ok(
resetConsumedIndex < connectingIndex && resetCompletedIndex < connectingIndex,
"manual reconnect must clear connect-automation consumption before the new session boot continues",
);
assert.ok(
autoElseIndex < autoSuppressIndex && autoSuppressIndex < connectingIndex,
"automatic reconnect must keep the existing connect-automation consumption decision",
);
assert.ok(
!source.slice(autoElseIndex, connectingIndex).includes("connectScriptsConsumedRef.current = false"),
"automatic reconnect must not reset connect-automation refs",
);
assert.match(
source.slice(autoElseIndex, connectingIndex),
/connectScriptsConsumedRef\.current = true/,
"automatic reconnect must stop the old batch without queuing it again",
);
});
test("restored disconnected sessions still create a terminal runtime before backend startup", () => {
const source = readFileSync(new URL("./useTerminalEffects.ts", import.meta.url), "utf8");
const runtimeIndex = source.indexOf("const runtime = createXTermRuntime");
const backendGateIndex = source.indexOf("if (!shouldStartTerminalBackend())");
assert.notEqual(runtimeIndex, -1);
assert.notEqual(backendGateIndex, -1);
assert.ok(
runtimeIndex < backendGateIndex,
"restored sessions need an xterm runtime before the backend starts",
);
});
test("auto reconnect prepares restored session state before clearing the restore marker", () => {
const source = readFileSync(new URL("./useTerminalEffects.ts", import.meta.url), "utf8");
const prepareIndex = source.indexOf("prepareRestoredReconnect?.()");
const updateConnectingIndex = source.indexOf('updateStatus("connecting")', prepareIndex);
assert.notEqual(prepareIndex, -1);
assert.notEqual(updateConnectingIndex, -1);
assert.ok(
prepareIndex < updateConnectingIndex,
"auto reconnect must capture restore details before the restored marker is cleared",
);
});
test("manual reconnect captures restore cwd intent before clearing restored state", () => {
const source = readFileSync(new URL("../Terminal.tsx", import.meta.url), "utf8");
const importIndex = source.indexOf("resolveRestoreCwdIntent");
const refIndex = source.indexOf("const restoreCwdIntentRef = useRef");
const contextIndex = source.indexOf("restoreCwdIntentRef,");
const prepareDefinitionIndex = source.indexOf("const prepareRestoredReconnect = useCallback");
const captureAssignIndex = source.indexOf("restoreCwdIntentRef.current =", prepareDefinitionIndex);
const captureCallIndex = source.indexOf("resolveRestoreCwdIntent", captureAssignIndex);
const reconnectIndex = source.indexOf("const startReconnect = ");
const manualBranchIndex = source.indexOf('if (mode === "manual")', reconnectIndex);
const manualPrepareIndex = source.indexOf("prepareRestoredReconnect();", manualBranchIndex);
const bootActiveIndex = source.indexOf("isBootActiveRef.current = true", manualPrepareIndex);
const connectingIndex = source.indexOf('updateStatus("connecting")', manualPrepareIndex);
const startNewSessionIndex = source.indexOf("const startNewSession = () =>", connectingIndex);
assert.notEqual(importIndex, -1);
assert.notEqual(refIndex, -1);
assert.notEqual(contextIndex, -1);
assert.notEqual(prepareDefinitionIndex, -1);
assert.notEqual(captureCallIndex, -1);
assert.notEqual(captureAssignIndex, -1);
assert.notEqual(reconnectIndex, -1);
assert.notEqual(manualBranchIndex, -1);
assert.notEqual(manualPrepareIndex, -1);
assert.notEqual(bootActiveIndex, -1);
assert.notEqual(connectingIndex, -1);
assert.notEqual(startNewSessionIndex, -1);
assert.ok(
captureAssignIndex < captureCallIndex && manualPrepareIndex < connectingIndex,
"manual retry must capture cwd intent while restoreState is still available",
);
assert.ok(
bootActiveIndex < startNewSessionIndex,
"manual retry must reactivate the boot guard before opening a backend session",
);
});
test("manual reconnect re-arms inherited cwd intent after a failed first connection", () => {
const source = readFileSync(new URL("../Terminal.tsx", import.meta.url), "utf8");
const reconnectIndex = source.indexOf("const startReconnect = ");
const manualBranchIndex = source.indexOf('if (mode === "manual")', reconnectIndex);
const manualPrepareIndex = source.indexOf("prepareRestoredReconnect();", manualBranchIndex);
const rearmIndex = source.indexOf("prepareInitialCwdIntent();", manualPrepareIndex);
const connectingIndex = source.indexOf('updateStatus("connecting")', manualPrepareIndex);
assert.notEqual(reconnectIndex, -1);
assert.notEqual(manualBranchIndex, -1);
assert.notEqual(manualPrepareIndex, -1);
assert.notEqual(rearmIndex, -1);
assert.notEqual(connectingIndex, -1);
assert.ok(
manualPrepareIndex < rearmIndex && rearmIndex < connectingIndex,
"a clone whose first connection fails must re-arm the inherited cwd on manual retry",
);
});
test("auto reconnect connected history ref is initialized after status state exists", () => {
const source = readFileSync(new URL("../Terminal.tsx", import.meta.url), "utf8");
const statusStateIndex = source.indexOf('const [status, setStatus] = useState<TerminalSession["status"]>');
const hasEverConnectedIndex = source.indexOf("const hasEverConnectedRef = useRef");
assert.notEqual(statusStateIndex, -1);
assert.notEqual(hasEverConnectedIndex, -1);
assert.ok(
statusStateIndex < hasEverConnectedIndex,
"auto reconnect refs must not read status before the status state is initialized",
);
});
test("reconnect wakes a hibernated terminal before requiring a terminal instance", () => {
const source = readFileSync(new URL("../Terminal.tsx", import.meta.url), "utf8");
const wakePromiseRefIndex = source.indexOf("const wakePromiseRef = useRef<Promise<boolean> | null>(null)");
const wakeGuardRefIndex = source.indexOf("const reconnectWakeInFlightRef = useRef(false)");
const wakeTokenRefIndex = source.indexOf("const reconnectWakeTokenRef = useRef<symbol | null>(null)");
const wakeInvalidateModeIndex = source.indexOf('const reconnectWakeInvalidateModeRef = useRef<"dispose" | "keep">("dispose")');
const wakeTokenCleanupIndex = source.indexOf("reconnectWakeTokenRef.current = null", wakeTokenRefIndex);
const reconnectIndex = source.indexOf("const startReconnect = ");
const hibernatedBranchIndex = source.indexOf('!termRef.current && hibernatedRef.current', reconnectIndex);
const duplicateWakeGuardIndex = source.indexOf("if (reconnectWakeInFlightRef.current) return", hibernatedBranchIndex);
const markWakeInFlightIndex = source.indexOf("reconnectWakeInFlightRef.current = true", duplicateWakeGuardIndex);
const connectingIndex = source.indexOf('updateStatus("connecting")', markWakeInFlightIndex);
const wakeCallIndex = source.indexOf("wakeHibernatedRuntimeForReconnectRef.current", hibernatedBranchIndex);
const wakeInvocationIndex = source.indexOf("void wakeForReconnect()", wakeCallIndex);
const wakeJoinIndex = source.indexOf("return wakePromiseRef.current ?? false", source.indexOf("const wakeFromHibernateRuntime"));
const wakeTokenIndex = source.indexOf("const wakeToken = Symbol()", hibernatedBranchIndex);
const staleWakeGuardIndex = source.indexOf("reconnectWakeTokenRef.current !== wakeToken", wakeInvocationIndex);
const staleWakeDisposeGuardIndex = source.indexOf('reconnectWakeInvalidateModeRef.current === "dispose"', staleWakeGuardIndex);
const staleWakeDisposeIndex = source.indexOf("disposeRuntimeOnly();", staleWakeDisposeGuardIndex);
const disconnectKeepModeIndex = source.indexOf('reconnectWakeInvalidateModeRef.current = "keep"', source.indexOf("const handleDisconnect"));
const missingTermGuardIndex = source.indexOf("if (!termRef.current) {", reconnectIndex);
const missingTermReturnIndex = source.indexOf("return;", missingTermGuardIndex);
assert.notEqual(wakePromiseRefIndex, -1);
assert.notEqual(wakeGuardRefIndex, -1);
assert.notEqual(wakeTokenRefIndex, -1);
assert.notEqual(wakeInvalidateModeIndex, -1);
assert.notEqual(wakeTokenCleanupIndex, -1);
assert.ok(wakeTokenCleanupIndex < reconnectIndex);
assert.notEqual(reconnectIndex, -1);
assert.notEqual(hibernatedBranchIndex, -1);
assert.notEqual(duplicateWakeGuardIndex, -1);
assert.notEqual(markWakeInFlightIndex, -1);
assert.notEqual(connectingIndex, -1);
assert.notEqual(wakeCallIndex, -1);
assert.notEqual(wakeInvocationIndex, -1);
assert.notEqual(wakeJoinIndex, -1);
assert.notEqual(wakeTokenIndex, -1);
assert.notEqual(staleWakeGuardIndex, -1);
assert.notEqual(staleWakeDisposeGuardIndex, -1);
assert.notEqual(staleWakeDisposeIndex, -1);
assert.notEqual(disconnectKeepModeIndex, -1);
assert.notEqual(missingTermGuardIndex, -1);
assert.notEqual(missingTermReturnIndex, -1);
assert.ok(
hibernatedBranchIndex < missingTermGuardIndex && wakeCallIndex < missingTermGuardIndex,
"manual and auto reconnect must wake fully hibernated sessions before the terminal guard can stop the retry",
);
assert.ok(
duplicateWakeGuardIndex < markWakeInFlightIndex && markWakeInFlightIndex < connectingIndex,
"hibernated reconnect must block duplicate requests before beginning an asynchronous wake",
);
assert.ok(
wakeTokenIndex < wakeInvocationIndex && wakeInvocationIndex < staleWakeGuardIndex,
"closing a terminal must be able to invalidate a pending hibernated reconnect",
);
assert.ok(
staleWakeGuardIndex < staleWakeDisposeGuardIndex && staleWakeDisposeGuardIndex < staleWakeDisposeIndex,
"an invalidated hibernated wake must dispose any runtime created after unmount cleanup",
);
assert.ok(
disconnectKeepModeIndex !== -1,
"disconnect must keep a woken runtime so later reconnect still has a terminal instance",
);
});
test("dismissing the disconnected dialog returns focus to the terminal for enter reconnect", () => {
const source = readFileSync(new URL("../Terminal.tsx", import.meta.url), "utf8");
const dismissIndex = source.indexOf("const handleDismissDisconnectedDialog = () =>");
const dismissedIndex = source.indexOf("setIsDisconnectedDialogDismissed(true)", dismissIndex);
const focusIndex = source.indexOf("queueMicrotask(() => termRef.current?.focus())", dismissIndex);
const closeSessionIndex = source.indexOf("const handleCloseDisconnectedSession = () =>", dismissIndex);
assert.notEqual(dismissIndex, -1);
assert.notEqual(dismissedIndex, -1);
assert.notEqual(focusIndex, -1);
assert.notEqual(closeSessionIndex, -1);
assert.ok(
dismissedIndex < focusIndex && focusIndex < closeSessionIndex,
"dismissing the disconnected dialog should leave Enter routed back through the terminal",
);
});
test("disconnected connection dialog keeps an Enter-reconnect focus sink", () => {
const source = readFileSync(new URL("./TerminalConnectionDialog.tsx", import.meta.url), "utf8");
assert.match(source, /canEnterReconnectFromDialog/);
assert.match(source, /data-terminal-disconnected-dialog/);
assert.match(source, /shouldReconnectDisconnectedDialogOnEnterKey/);
assert.match(source, /shouldClaimDisconnectedDialogFocus/);
assert.match(source, /shouldRestoreDisconnectedDialogTerminalFocus/);
assert.match(source, /restoreTerminalFocusFromDisconnectedDialog/);
assert.match(source, /dialogFocusRef\.current\?\.focus/);
// Focus claim must key only on enter-reconnect mode, not showLogs/error,
// or toggling "Show logs" steals keyboard focus off the button.
const claimEffectIdx = source.indexOf("Claim focus only when Enter-reconnect mode turns on");
// Restore must run on overlay unmount — not when Enter-reconnect ends into
// auth/host-key while the dialog stays mounted.
const restoreEffectIdx = source.indexOf("Restore xterm focus only when this overlay unmounts");
assert.notEqual(claimEffectIdx, -1);
assert.notEqual(restoreEffectIdx, -1);
const claimDeps = source.indexOf("}, [canEnterReconnectFromDialog, isFocusedPane]);", claimEffectIdx);
const restoreDeps = source.indexOf("}, []);", restoreEffectIdx);
assert.notEqual(claimDeps, -1);
assert.notEqual(restoreDeps, -1);
assert.ok(claimDeps < restoreEffectIdx);
// Claim deps must not re-run on showLogs/error/status; restore is unmount-only.
assert.match(source.slice(claimDeps, claimDeps + 60), /^\}, \[canEnterReconnectFromDialog, isFocusedPane\]\);/);
assert.match(source.slice(restoreDeps, restoreDeps + 10), /^\}, \[\]\);/);
assert.equal(source.includes("}, [canEnterReconnectFromDialog, status, error, showLogs]"), false);
// Split unfocused panes pass isFocusedPane so document blur cannot steal focus.
assert.match(source, /isFocusedPane/);
assert.match(source, /shouldClaimDisconnectedDialogFocus\(\{[\s\S]*isFocusedPane/);
// Unmount restore must also honor isFocusedPane when focus lands on body/html.
assert.match(source, /restoreTerminalFocusFromDisconnectedDialog\(\{[\s\S]*isFocusedPane/);
});
test("open terminal search does not globally gate enter reconnect", () => {
const viewSource = readFileSync(new URL("./TerminalView.tsx", import.meta.url), "utf8");
const gateIndex = viewSource.indexOf("export function shouldReconnectTerminalOnEnterKey");
const bodyEnd = viewSource.indexOf("export function shouldBlockTerminalReconnectForTarget", gateIndex);
const gateBody = viewSource.slice(gateIndex, bodyEnd);
assert.equal(gateBody.includes("!isSearchOpen"), false);
assert.match(viewSource, /isTerminalSearchInput/);
assert.match(
readFileSync(new URL("./TerminalSearchBar.tsx", import.meta.url), "utf8"),
/data-terminal-search-input/,
);
});
test("terminal view receives the effective compose bar state for enter reconnect gating", () => {
const source = readFileSync(new URL("../Terminal.tsx", import.meta.url), "utf8");
const effectiveDefinitionIndex = source.indexOf("const effectiveComposeBarOpen =");
const viewIndex = source.indexOf("<TerminalView ctx={{");
const passEffectiveIndex = source.indexOf("isComposeBarOpen: effectiveComposeBarOpen", viewIndex);
assert.notEqual(effectiveDefinitionIndex, -1);
assert.notEqual(viewIndex, -1);
assert.notEqual(passEffectiveIndex, -1);
assert.ok(
effectiveDefinitionIndex < passEffectiveIndex,
"TerminalView must use the visible workspace compose state before deciding whether Enter can reconnect",
);
});
test("startup and attach cwd cache clears preserve restore cwd metadata", () => {
const terminalSource = readFileSync(new URL("../Terminal.tsx", import.meta.url), "utf8");
const effectsSource = readFileSync(new URL("./useTerminalEffects.ts", import.meta.url), "utf8");
const clearDefinitionIndex = terminalSource.indexOf("const clearTerminalCwd = useCallback");
const clearNotifyIndex = terminalSource.indexOf("onTerminalCwdChange?.(sessionId, null)", clearDefinitionIndex);
const persistGuardIndex = terminalSource.indexOf("persistRestoreMetadata", clearDefinitionIndex);
const attachIndex = terminalSource.indexOf("onSessionAttached: (id: string) =>");
const attachClearIndex = terminalSource.indexOf("clearTerminalCwd({ persistRestoreMetadata: false })", attachIndex);
const startupClearIndex = effectsSource.indexOf("clearTerminalCwd({ persistRestoreMetadata: false })");
assert.notEqual(clearDefinitionIndex, -1);
assert.notEqual(clearNotifyIndex, -1);
assert.notEqual(persistGuardIndex, -1);
assert.notEqual(attachIndex, -1);
assert.notEqual(attachClearIndex, -1);
assert.notEqual(startupClearIndex, -1);
assert.ok(
persistGuardIndex < clearNotifyIndex,
"clearTerminalCwd must gate persisted restore metadata updates",
);
});
test("restored cwd intent marks known cwd before initial backend pwd probe can persist home", () => {
const terminalSource = readFileSync(new URL("../Terminal.tsx", import.meta.url), "utf8");
const effectsSource = readFileSync(new URL("./useTerminalEffects.ts", import.meta.url), "utf8");
const callbackIndex = terminalSource.indexOf("onRestoreCwdIntentConsumed:");
const knownAssignIndex = terminalSource.indexOf("knownCwdRef.current = cwd", callbackIndex);
const backendProbeGuardIndex = effectsSource.indexOf("knownCwdRef.current");
const backendPwdWriteIndex = effectsSource.indexOf("onPluginRuntimeCwdChange(result.cwd)", backendProbeGuardIndex);
assert.notEqual(callbackIndex, -1);
assert.notEqual(knownAssignIndex, -1);
assert.notEqual(backendProbeGuardIndex, -1);
assert.notEqual(backendPwdWriteIndex, -1);
assert.ok(
knownAssignIndex > callbackIndex,
"Terminal must preserve the restore target as a known cwd when the restore command is sent",
);
assert.ok(
backendProbeGuardIndex < backendPwdWriteIndex,
"initial backend pwd probe must remain guarded by knownCwdRef before it publishes the probed cwd",
);
});