fix(mobile): wait for adversarial review content and route readiness

Wait for the diff body rather than just Review controls, and handle Back through Source Control before opening file previews. iOS confirms terminal links and rendered adversarial diff; the full unattended journey remains tracked. Required gates pass in native-chat-read-gates.
This commit is contained in:
Jinwoo-H
2026-09-06 19:01:27 -04:00
parent a0eee2fc21
commit 79f54a8b49
4 changed files with 27 additions and 3 deletions
@@ -69,6 +69,7 @@ export function createHostedIosAdversarialContentInspector({ emulator, fixture,
observations.push(
await captureHostedWebViewAdversarialObservation({
document,
expectedMarker: phase === 'review' ? HOSTED_ADVERSARIAL_CONTENT_MARKER : undefined,
timeoutMs: Math.min(timeoutMs, 15_000)
})
)
@@ -55,7 +55,9 @@ export async function captureHostedWebViewAdversarialObservation({
}
await delay(250)
} while (Date.now() < deadline)
throw new Error(`Hosted adversarial marker was not rendered: ${expectedMarker}`)
throw new Error(
`Hosted adversarial marker was not rendered: ${expectedMarker}; state=${JSON.stringify(observation?.state).slice(0, 4000)}`
)
}
export function hostedWebViewAdversarialContentObservations(observations) {
@@ -34,7 +34,7 @@ export async function inspectHostedWebViewAdversarialFiles({
}) {
const reviewRoute = routeFromDocument(document, 'review')
await activateHostedWebViewControl(document, { kind: 'label', value: 'Back' }, WebSocketCtor)
await waitForRoute(document, '/session/', 'tabs', timeoutMs, WebSocketCtor)
await returnToSession(document, timeoutMs, WebSocketCtor)
await waitForLabel(document, 'Open file explorer', timeoutMs, WebSocketCtor)
await activateHostedWebViewControl(
document,
@@ -87,6 +87,25 @@ export async function inspectHostedWebViewAdversarialFiles({
}
}
async function returnToSession(document, timeoutMs, WebSocketCtor) {
const deadline = Date.now() + timeoutMs
while (Date.now() < deadline) {
const state = await readHostedWebViewState(document, WebSocketCtor)
if (state.href.includes('/session/')) {
return
}
if (state.href.includes('/source-control/') && state.labels.includes('Back to session')) {
await activateHostedWebViewControl(
document,
{ kind: 'label', value: 'Back to session' },
WebSocketCtor
)
}
await delay(250)
}
throw new Error('Hosted adversarial journey did not return to Session')
}
async function openPreview(document, filename, timeoutMs, WebSocketCtor) {
const label = `Preview file ${filename}`
await waitForLabel(document, label, timeoutMs, WebSocketCtor)
@@ -327,7 +327,9 @@ async function main() {
await captureHostedWebViewAdversarialObservation({
document,
expectedMarker:
phase === 'sessionDiff' ? HOSTED_ADVERSARIAL_CONTENT_MARKER : undefined,
phase === 'sessionDiff' || phase === 'review'
? HOSTED_ADVERSARIAL_CONTENT_MARKER
: undefined,
timeoutMs: Math.min(options.timeoutMs, 15_000)
})
)