Repository navigation
feat(snapshot): interpret iOS node facts once at producer seams - #3592
Conversation
|
Native-state validation at Focused regressionpnpm exec vitest run --project unit-core packages/platform-apple/src/snapshot-source/tree.test.ts packages/provider-webdriver/src/webdriver-source.test.ts packages/provider-webdriver/src/webdriver-ios-snapshot.test.ts packages/provider-limrun/src/snapshot.test.ts packages/provider-limrun/src/ios-snapshot-adapter.test.ts
# base f70224ad8 producer bodies: 38 failed, 54 passed
# fixed: 92 passed
pnpm exec vitest run packages/capture-kit/src/ios-native-states.test.ts packages/platform-apple/src/snapshot-source/tree.test.ts packages/provider-webdriver/src/webdriver-source.test.ts packages/provider-webdriver/src/webdriver-ios-snapshot.test.ts packages/provider-limrun/src/snapshot.test.ts packages/provider-limrun/src/ios-snapshot-adapter.test.ts
# six files / 112 tests passed
pnpm exec vitest run --project unit-core scripts/__tests__/eager-closure-budgets.test.ts
# 842 passed at 6478cc829; TypeScript source is identical at current head
node --experimental-strip-types scripts/swift-toolchain-tmpdir.ts swift test --package-path apple/snapshot-presentation
# 21 passed at 39b2dc634; this package's source/test diff to current head is emptyThe Swift and TypeScript decoders replay the same 20-case native-state golden. Raw booleans (including false) override inferred values, unknown values remain absent, and a disabled field retains its native editable capability while Cold review findings (private-AX acquisition, radio/toggle type map, relocation import, blocking modal acquisition) were fixed at their owners. Modal and single-node query serializers now have native XCTest coverage. Final independent review reports no remaining findings. No layering policy or allowlist was changed; the export declaration includes the new pure acquisition subpath. Apple compile evidenceAt this head: selection passes (438 declared, 0 unreachable); iOS unit-enabled and macOS builds both report TEST BUILD SUCCEEDED. Incremental builds reused the previously clean-built DerivedData; their isolation scan covers recompiled files. pnpm check:xctest-selection
AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS=1 AGENT_DEVICE_XCUITEST_DESTINATION='platform=iOS Simulator,id=BC54AC11-CD86-4C10-8C4E-5101C773FFEB' AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH=/private/tmp/issue-3524-unit-derived pnpm build:xcuitest:ios
AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH=/private/tmp/issue-3524-macos-derived pnpm build:xcuitest:macos
pnpm check:affected --runEarlier clean iOS production, iOS unit-enabled, and macOS builds passed at Android Appium liveCreated private AVD
[
{
"type": "android.widget.CheckBox",
"label": "Native unchecked",
"checked": false,
"password": false,
"enabled": true,
"heading": false,
"effectiveEditable": false
},
{
"type": "android.widget.CheckBox",
"label": "Native checked",
"checked": true,
"password": false,
"enabled": true,
"heading": false,
"effectiveEditable": false
},
{
"type": "android.widget.EditText",
"label": "Native editable",
"password": false,
"enabled": true,
"heading": false,
"effectiveEditable": true
},
{
"type": "android.widget.EditText",
"label": "Native password",
"password": true,
"enabled": true,
"heading": false,
"effectiveEditable": true
},
{
"type": "android.widget.EditText",
"label": "Native disabled",
"password": false,
"enabled": false,
"heading": false,
"effectiveEditable": false
},
{
"type": "android.widget.TextView",
"label": "Native heading",
"password": false,
"enabled": true,
"heading": true,
"effectiveEditable": false
},
{
"type": "android.widget.TextView",
"label": "Native States",
"password": false,
"enabled": true,
"heading": false,
"effectiveEditable": false
}
]
Limrun live acquisitionset -a && . /Users/michal/Developer/agent-device/.env && set +a
node --experimental-strip-types /private/tmp/issue-3524-limrun-live.tsCredentials were available, never copied into the worktree or printed. At {"producer":"limrun-ios-tree","type":"TextField","editable":true,"password":false,"roleDescription":"search text field"}
Local iOS runtime blockerAGENT_DEVICE_STATE_DIR=/private/tmp/issue-3524-state node bin/agent-device.mjs device status --platform ios --udid BC54AC11-CD86-4C10-8C4E-5101C773FFEB --jsonThe assigned iPhone17Pro simulator is classified live, owned by session Live verifier source: webdriver.tsimport {parseWebDriverSourceFacts} from '/Users/michal/.t3/worktrees/agent-device/issue-3524/packages/provider-webdriver/src/webdriver-source.ts';
import {acquireWebDriverIosSnapshot} from '/Users/michal/.t3/worktrees/agent-device/issue-3524/packages/provider-webdriver/src/webdriver-ios-snapshot.ts';
import {attachRefs} from '/Users/michal/.t3/worktrees/agent-device/issue-3524/packages/kernel/src/snapshot.ts';
import {isNodeEditable} from '/Users/michal/.t3/worktrees/agent-device/issue-3524/packages/selectors/src/internal/node.ts';
import {writeFileSync} from 'node:fs';
import assert from 'node:assert/strict';
const platform=process.argv[2] as 'android'|'ios';
const caps=platform==='android' ? {
platformName:'Android','appium:automationName':'UiAutomator2','appium:udid':'emulator-5560','appium:systemPort':8260,'appium:noReset':true,'appium:appPackage':'com.callstack.issue3524','appium:appActivity':'.NativeStates',
} : {
platformName:'iOS','appium:automationName':'XCUITest','appium:udid':'BC54AC11-CD86-4C10-8C4E-5101C773FFEB','appium:noReset':true,'appium:autoLaunch':false,
'appium:derivedDataPath':'/private/tmp/issue-3524-wda','appium:wdaLocalPort':8134,'appium:bundleId':'com.callstack.agentdevice.runner',
};
let id:string|undefined;
const base='http://127.0.0.1:4734';
async function request(path:string,body?:unknown) {
const resp=await fetch(base+path,{method:body===undefined?'GET':'POST',headers:{'content-type':'application/json'},body:body===undefined?undefined:JSON.stringify(body)});
const json=await resp.json() as any;
if(!resp.ok)throw new Error(JSON.stringify(json));return json.value;
}
try {
const result=await request('/session',{capabilities:{alwaysMatch:caps,firstMatch:[{}]}});id=result.sessionId;
const xml=await request(`/session/${id}/source`) as string;
writeFileSync(`/private/tmp/issue-3524-${platform}-appium.xml`,xml);
const nodes=attachRefs(platform==='ios' ? acquireWebDriverIosSnapshot(xml).acquisition.nodes : parseWebDriverSourceFacts(xml,platform).nodes);
const states=nodes.filter(n=>/Native|native-/.test(`${n.label??''} ${n.identifier??''} ${n.placeholder??''}`)).map(n=>({type:n.type,label:n.label,id:n.identifier,checked:n.checked,password:n.password,editable:n.editable,enabled:n.enabled,heading:n.heading,effectiveEditable:isNodeEditable(n,platform)}));
console.log(JSON.stringify(states,null,2));
if(platform==='android') {
assert.equal(nodes.find(n=>n.label==='Native unchecked')?.checked,false);
assert.equal(nodes.find(n=>n.label==='Native checked')?.checked,true);
assert.equal(nodes.find(n=>n.label==='Native password')?.password,true);
const disabled=nodes.find(n=>n.label==='Native disabled');assert.ok(disabled);assert.equal(isNodeEditable(disabled,platform),false);
assert.equal(nodes.find(n=>n.label==='Native heading')?.heading,true);
} else {
assert.equal(nodes.find(n=>n.identifier==='native-off')?.checked,false);
assert.equal(nodes.find(n=>n.identifier==='native-on')?.checked,true);
assert.equal(nodes.find(n=>n.identifier==='native-secure')?.password,true);
const disabled=nodes.find(n=>n.identifier==='native-disabled');assert.ok(disabled);assert.equal(disabled.editable,true);assert.equal(isNodeEditable(disabled,platform),false);
}
console.log(`${platform} Appium native-state assertions passed`);
} finally {if(id){await fetch(`${base}/session/${id}`,{method:'DELETE'});console.log('Appium session deleted');}}Live verifier source: limrun-live.tsimport Limrun from '/Users/michal/.t3/worktrees/agent-device/issue-3524/node_modules/@limrun/api/index.mjs';
import { createInstanceClient } from '/Users/michal/.t3/worktrees/agent-device/issue-3524/node_modules/@limrun/api/ios-client.mjs';
import { captureLimrunIosSnapshot } from '/Users/michal/.t3/worktrees/agent-device/issue-3524/packages/provider-limrun/src/ios-snapshot-adapter.ts';
import assert from 'node:assert/strict';
const api = new Limrun({ apiKey: process.env.LIMRUN_API_KEY, timeout: 120000, maxRetries: 0 });
let instance, client;
try {
instance = await api.iosInstances.create({ wait: true, metadata: { displayName: 'issue-3524-final-acquisition' }, spec: { inactivityTimeout: '3m', hardTimeout: '10m' } });
client = await createInstanceClient({ apiUrl: instance.status.apiUrl, token: instance.status.token, logLevel: 'warn' });
await client.launchApp('com.apple.Preferences');
const result = await captureLimrunIosSnapshot({ client, instanceId: instance.metadata.id });
assert.equal(result.stage, 'acquired');
assert.equal(result.acquisition.producer, 'limrun-ios-tree');
const field = result.acquisition.nodes.find(node => node.type === 'TextField');
assert.ok(field);
assert.equal(field.editable, true);
assert.equal(field.password, false);
assert.equal(field.roleDescription, 'search text field');
console.log(JSON.stringify({ producer: result.acquisition.producer, type: field.type, editable: field.editable, password: field.password, roleDescription: field.roleDescription }));
console.log('Live Limrun acquisition adapter native-state assertions passed');
} finally {
client?.disconnect();
if (instance) { await api.iosInstances.delete(instance.metadata.id); console.log('Owned Limrun instance deleted'); }
}Final |
There was a problem hiding this comment.
All reported issues were addressed across 30 files
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
iOS runtime follow-up — passedVerified on iPhone 17, iOS 27.0, UDID
The worktree already had current dependencies and a CLI build newer than CommandsThe cd /Users/michal/.t3/worktrees/agent-device/issue-3524
git fetch origin
git rev-parse HEAD
xcrun simctl list devices booted
export AGENT_DEVICE_STATE_DIR=/private/tmp/issue-3524-ios-followup-state
export AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH=/Users/michal/.t3/worktrees/agent-device/issue-3524/.tmp/ios-followup-derived
ad() {
node bin/agent-device.mjs "$@" --platform ios \
--udid 0E0FCBDC-88E2-4CB6-A1DA-E148F77743F4 \
--session issue3524-ios-followup --json
}
node -e 0
node bin/agent-device.mjs device status --stale --json
# Runner preparation used the shared host lock:
while ! mkdir /tmp/agent-device-host-gate.lock 2>/dev/null; do sleep 20; done
trap 'rmdir /tmp/agent-device-host-gate.lock' EXIT
ad prepare ios-runner --timeout 240000
rmdir /tmp/agent-device-host-gate.lock
trap - EXIT
# Repo-owned --agent-device-native-states fixture, installed with a separate app identity:
ditto .tmp/ios-followup-derived/Build/Products/Debug-iphonesimulator/AgentDeviceRunner.app \
/private/tmp/issue-3524-ios-followup-evidence/NativeStates.app
/usr/libexec/PlistBuddy -c 'Set :CFBundleIdentifier com.callstack.issue3524.nativefacts' \
/private/tmp/issue-3524-ios-followup-evidence/NativeStates.app/Info.plist
codesign --force --sign - /private/tmp/issue-3524-ios-followup-evidence/NativeStates.app
ad install com.callstack.issue3524.nativefacts /private/tmp/issue-3524-ios-followup-evidence/NativeStates.app
ad open com.callstack.issue3524.nativefacts --relaunch --launch-args=--agent-device-native-states
ad snapshot --debug # Simulator AX bridge
ad snapshot --actions --debug # Apple runner private AX acquisition
ad snapshot --observe-only --debug # Apple runner XCTest recursive tree acquisition
ad is editable 'id=native-field'
ad is editable 'id=native-disabled' # Expected predicate_failed
node bin/agent-device.mjs device status --jsonDirect attempts to use the runner-host bundle ID as the fixture target failed during setup (app unavailable and one XCTest early bootstrap exit). A retry of runner preparation passed. Installing the same repo fixture under Producer proof and observed JSONDefault capture request {"phase":"ios.snapshot-source.acquire","data":{"producer":"simulator-ax-bridge"}}
{"phase":"ios.snapshot-source.present","data":{"producer":"simulator-ax-bridge"}}
{"snapshotQuality":{"state":"recovered","backend":"private-ax","reasonCode":"requested-backend"}}
{"snapshotQuality":{"state":"healthy","backend":"tree"},"observation":{"mode":"observe-only","activationPerformed":false,"appState":"runningForeground","appStateSource":"xcuiapplication-state"}}Below are projected excerpts from each actual JSON capture; omitted geometry/refs/container nodes are not part of this projection. Static text has no Simulator AX bridge {"type":"Switch","identifier":"native-off","value":"0","checked":false,"enabled":true}
{"type":"Switch","identifier":"native-on","value":"1","checked":true,"enabled":true}
{"type":"SecureTextField","identifier":"native-secure","value":"Native input","password":true,"editable":true,"enabled":true}
{"type":"TextField","identifier":"native-field","value":"Native input","password":false,"editable":true,"enabled":true}
{"type":"TextField","identifier":"native-disabled","value":"Native input","password":false,"editable":true,"enabled":false}
{"type":"StaticText","label":"Agent Device Runner","enabled":true}Apple runner — private AX {"type":"Switch","identifier":"native-off","value":"0","checked":false,"enabled":true}
{"type":"Switch","identifier":"native-on","value":"1","checked":true,"enabled":true}
{"type":"SecureTextField","identifier":"native-secure","password":true,"editable":true,"enabled":true}
{"type":"TextField","identifier":"native-field","password":false,"editable":true,"enabled":true}
{"type":"TextField","identifier":"native-disabled","password":false,"editable":true,"enabled":false}
{"type":"StaticText","label":"Agent Device Runner","enabled":true}Apple runner — recursive XCTest tree {"type":"Switch","identifier":"native-off","label":"0","value":"0","checked":false,"enabled":true}
{"type":"Switch","identifier":"native-on","label":"1","value":"1","checked":true,"enabled":true}
{"type":"SecureTextField","identifier":"native-secure","password":true,"editable":true,"enabled":true}
{"type":"TextField","identifier":"native-field","password":false,"editable":true,"enabled":true}
{"type":"TextField","identifier":"native-disabled","password":false,"editable":true,"enabled":false}
{"type":"StaticText","label":"Agent Device Runner","enabled":true}All three captures passed assertions for both switch values, secure-password truth, enabled-field editability, disabled-field {"success":true,"data":{"predicate":"editable","pass":true,"selector":"id=native-field"}}
{"success":false,"error":{"code":"COMMAND_FAILED","details":{"reason":"predicate_failed","predicate":"editable","selector":"id=native-disabled","predicateDetails":"actual={\"visible\":true,\"editable\":false,\"selected\":false,\"focused\":false}"}}}Cleanup and remaining scopead close
node bin/agent-device.mjs daemon stop --state-dir /private/tmp/issue-3524-ios-followup-state --clean --json
xcrun simctl uninstall 0E0FCBDC-88E2-4CB6-A1DA-E148F77743F4 com.callstack.issue3524.nativefacts
xcrun simctl uninstall 0E0FCBDC-88E2-4CB6-A1DA-E148F77743F4 com.callstack.agentdevice.runner
node bin/agent-device.mjs device status --json
git status --short
git rev-parse HEADClose and clean daemon stop succeeded with no warnings or orphaned claims. The assigned simulator has no remaining claim; |
|
Size Report
Startup median (7 runs, lower is better):
|
|
The code looks fine at fb7e850, but one required live check is still missing, so this is not ready to merge. Issue #3524 lists Appium iOS as a required live producer. The Appium iOS path now runs real XCUITest page source through acquireIosNativeStates, but it has only been tested against hand-built XML. The earlier live runs cover the Simulator AX bridge and the runner, not Appium iOS. That leaves the Appium claims for switch Could the facts be derived in the owners instead of at nine call sites? In TS, The two open inline threads below are only a pointer; the one cubic-dev-ai P3 thread on SnapshotModelsTests.swift (#3592 (comment)) is fixed at this head, so please resolve it. Not blocking, and you can take or leave these: the snapshots doc names only iOS, but the runner derivation is not Smoke Tests, Mutants and Coverage are still pending with no failure so far. The diff touches the Smoke route and the snapshot-occlusion mutant scope, so any failure there needs checking against this diff. I did not run any tests, and I could not confirm which attributes the real UiAutomator2, XCUITest and limrun sources emit beyond your posted outputs. The macOS runner behavior is inferred from the missing gates, not observed live. Next, please post the Appium iOS capture, then let the pending jobs finish. |
Round 3 response
{"producer":"limrun-ios-tree"}
{"type":"CheckBox","identifier":"native-off","value":"0","checked":false,"enabled":true}
{"type":"CheckBox","identifier":"native-on","value":"1","checked":true,"enabled":true}
{"type":"TextField","identifier":"native-secure","password":true,"editable":true,"enabled":true,"roleDescription":"secure text field"}
{"type":"TextField","identifier":"native-field","password":false,"editable":true,"enabled":true}
{"type":"TextField","identifier":"native-disabled","password":false,"editable":true,"enabled":false}
{"type":"StaticText","label":"Agent Device Runner","enabled":true}
{"type":"Heading","label":"Settings","heading":true,"roleDescription":"heading"}
The required post-refactor local runner/private-AX/tree and Simulator AX live reruns are blocked by the same live device owner. Prior round-2 live evidence remains attributed to fb7e850 and is not claimed as validation of this Swift refactor. Runtime head Executed verification commandscd /Users/michal/.t3/worktrees/agent-device/issue-3524
pnpm check:xctest-selection
node --experimental-strip-types scripts/swift-toolchain-tmpdir.ts swift test --package-path apple/snapshot-presentation
AGENT_DEVICE_XCUITEST_DESTINATION='platform=iOS Simulator,id=0E0FCBDC-88E2-4CB6-A1DA-E148F77743F4' AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH=/private/tmp/issue-3524-r3-ios-derived AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS=1 pnpm build:xcuitest:ios
AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH=/private/tmp/issue-3524-r3-macos-derived pnpm build:xcuitest:macos
pnpm check:affected --run
pnpm exec vitest run scripts/__tests__/eager-closure-budgets.test.ts scripts/__tests__/test-file-size-ratchet.test.ts
set -a && . /Users/michal/Developer/agent-device/.env && set +a
node --experimental-strip-types /private/tmp/issue-3524-r3-limrun-fixture.mjsThe final limrun script below ran against the separately identified import Limrun from '/Users/michal/.t3/worktrees/agent-device/issue-3524/node_modules/@limrun/api/index.mjs';
import {createInstanceClient} from '/Users/michal/.t3/worktrees/agent-device/issue-3524/node_modules/@limrun/api/ios-client.mjs';
import {captureLimrunIosSnapshot} from '/Users/michal/.t3/worktrees/agent-device/issue-3524/packages/provider-limrun/src/ios-snapshot-adapter.ts';
import {writeFileSync} from 'node:fs';
import assert from 'node:assert/strict';
const api=new Limrun({apiKey:process.env.LIMRUN_API_KEY,timeout:120000,maxRetries:0});
let instance,client,asset;
try {
asset=await api.assets.getOrUpload({path:'/private/tmp/issue-3524-r3-evidence/NativeStates.zip',name:'issue-3524-round3-'+Date.now()+'.zip',platform:'ios',ttl:'1h'});
instance=await api.iosInstances.create({wait:true,metadata:{displayName:'issue-3524-round3-native-fixture'},spec:{inactivityTimeout:'3m',hardTimeout:'10m'}});
client=await createInstanceClient({apiUrl:instance.status.apiUrl,token:instance.status.token,logLevel:'warn'});
console.log('Fixture installation',JSON.stringify(await client.installApp(asset.signedDownloadUrl)));
const launch=await client.simctl(['launch','--terminate-running-process','booted','com.callstack.issue3524.nativefacts','--agent-device-native-states']).wait();
console.log('Fixture launch',JSON.stringify({code:launch.code,stdout:launch.stdout.toString(),stderr:launch.stderr.toString()}));
assert.equal(launch.code,0);
const raw=await client.elementTree();writeFileSync('/private/tmp/issue-3524-r3-evidence/limrun-fixture-raw.json',JSON.stringify(raw));
const result=await captureLimrunIosSnapshot({client,instanceId:instance.metadata.id});writeFileSync('/private/tmp/issue-3524-r3-evidence/limrun-fixture.json',JSON.stringify(result));
const nodes=result.acquisition.nodes;
console.log(JSON.stringify(nodes.map(n=>({type:n.type,id:n.identifier,label:n.label,value:n.value,checked:n.checked,password:n.password,editable:n.editable,enabled:n.enabled,roleDescription:n.roleDescription})).filter(n=>n.id?.startsWith('native-')||n.type==='StaticText')));
assert.equal(nodes.find(n=>n.identifier==='native-off')?.checked,false);assert.equal(nodes.find(n=>n.identifier==='native-on')?.checked,true);
const secure=nodes.find(n=>n.identifier==='native-secure');assert.ok(secure);assert.equal(secure.password,true);assert.equal(secure.editable,true);
console.log('Limrun fixture native-state assertions passed');
}finally{client?.disconnect();if(instance){await api.iosInstances.delete(instance.metadata.id);console.log('Owned instance deleted');}if(asset){await api.assets.delete(asset.id);console.log('Owned asset deleted');}} |
There was a problem hiding this comment.
All reported issues were addressed across 16 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Round 4 — Appium iOS and post-refactor native live checks passed@thymikee — this addresses the blocking Appium requirement in #3592 (comment) and repeats the round-2 native checks after the constructor refactor. All captures ran on unchanged head Build and fixture commandscd /Users/michal/.t3/worktrees/agent-device/issue-3524
pnpm install --frozen-lockfile
pnpm build
xcrun simctl create tmp-issue-3524-r4 com.apple.CoreSimulator.SimDeviceType.iPhone-17 com.apple.CoreSimulator.SimRuntime.iOS-27-0
xcrun simctl boot B66F0347-E40B-406C-AADC-72D95C2E2810
xcrun simctl bootstatus B66F0347-E40B-406C-AADC-72D95C2E2810 -b
# Apple build and preparation used this host-lock wrapper:
while ! mkdir /tmp/agent-device-host-gate.lock 2>/dev/null; do sleep 20; done
trap 'rmdir /tmp/agent-device-host-gate.lock 2>/dev/null' EXIT
AGENT_DEVICE_XCUITEST_DESTINATION='platform=iOS Simulator,id=B66F0347-E40B-406C-AADC-72D95C2E2810' AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH=/private/tmp/issue-3524-r4-ios-derived AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS=1 pnpm build:xcuitest:ios
# Wrapper releases lock on command-shell exit, including failure.
ditto /private/tmp/issue-3524-r4-ios-derived/Build/Products/Debug-iphonesimulator/AgentDeviceRunner.app /private/tmp/issue-3524-r4-evidence/NativeStates.app
/usr/libexec/PlistBuddy -c 'Set :CFBundleIdentifier com.callstack.issue3524.nativefacts' /private/tmp/issue-3524-r4-evidence/NativeStates.app/Info.plist
codesign --force --sign - /private/tmp/issue-3524-r4-evidence/NativeStates.app
xcrun simctl install B66F0347-E40B-406C-AADC-72D95C2E2810 /private/tmp/issue-3524-r4-evidence/NativeStates.appAppium iOS THROUGH THE DAEMONRead export default () => ({webDriver:{"provider": "native-appium", "endpoint": "http://127.0.0.1:4736", "platform": "ios", "deviceName": "iPhone 17", "profileFields": {"provider": "native-appium", "label": "Native Appium", "fields": {"providerApp": "consumed", "providerOsVersion": "refused", "providerDeviceType": "refused", "providerProject": "refused", "providerBuild": "refused", "providerSessionName": "refused", "providerDeviceOrientation": "refused", "providerGeoLocation": "refused", "providerTimezone": "refused", "providerAppiumVersion": "refused", "providerLanguage": "refused", "providerLocale": "refused", "providerNetworkProfile": "refused", "providerCustomNetwork": "refused", "providerNoResignApp": "refused", "awsProjectArn": "refused", "awsDeviceArn": "refused", "awsAppArn": "refused", "awsRegion": "refused", "awsInteractionMode": "refused"}}, "requestPolicy": {"retryAttempts": 0, "timeoutMs": 240000}, "webdriverCapabilities": {"platformName": "iOS", "appium:automationName": "XCUITest", "appium:udid": "B66F0347-E40B-406C-AADC-72D95C2E2810", "appium:bundleId": "com.callstack.issue3524.nativefacts", "appium:noReset": true, "appium:autoLaunch": true, "appium:processArguments": {"args": ["--agent-device-native-states"]}, "appium:derivedDataPath": "/private/tmp/issue-3524-r4-wda", "appium:wdaLocalPort": 8136}},connection:{resolve:()=>({profile:{leaseProvider:"native-appium",platform:"ios"}}),verify:async()=>({provider:"native-appium",status:"ok"})}});APPIUM_HOME=/private/tmp/issue-3524-appium-home /private/tmp/issue-3524-appium/node_modules/.bin/appium --address 127.0.0.1 --port 4736 --log-timestamp --log /private/tmp/issue-3524-r4-appium-server.log
# Separate command shell, same worktree:
export AGENT_DEVICE_HOME=/private/tmp/issue-3524-r4-appium-home
export AGENT_DEVICE_STATE_DIR=/private/tmp/issue-3524-r4-appium-state
node bin/agent-device.mjs connect native-appium --platform ios --session issue3524-r4-appium --json
node bin/agent-device.mjs open com.callstack.issue3524.nativefacts --platform ios --session issue3524-r4-appium --timeout 240000 --json
node bin/agent-device.mjs snapshot --platform ios --session issue3524-r4-appium --debug --json > /private/tmp/issue-3524-r4-evidence/appium.jsonDaemon request Two setup attempts were corrected before the successful capture: the copied temporary plugin initially refused app selection, and a no-app open did not supply Appium's required bundleId. The final plugin consumes app selection and the successful command names the fixture explicitly. Neither was a native-state regression. Actual Appium JSON projection: {"type":"XCUIElementTypeTextField","identifier":"native-field","label":"native-field","value":"Native input","password":false,"editable":true,"enabled":true}
{"type":"XCUIElementTypeSecureTextField","identifier":"native-secure","label":"native-secure","value":"Native input","password":true,"editable":true,"enabled":true}
{"type":"XCUIElementTypeTextField","identifier":"native-disabled","label":"native-disabled","value":"Native input","password":false,"editable":true,"enabled":false}
{"type":"XCUIElementTypeSwitch","identifier":"native-off","label":"native-off","value":"0","checked":false,"enabled":true}
{"type":"XCUIElementTypeSwitch","identifier":"native-on","label":"native-on","value":"1","checked":true,"enabled":true}
{"type":"XCUIElementTypeStaticText","identifier":"Agent Device Runner","label":"Agent Device Runner","value":"Agent Device Runner","enabled":true}Native checks repeated on this head# Separate shell without the Appium AGENT_DEVICE_HOME:
export AGENT_DEVICE_STATE_DIR=/private/tmp/issue-3524-r4-native-state
export AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH=/Users/michal/.t3/worktrees/agent-device/issue-3524/.tmp/r4-native-derived
export AGENT_DEVICE_XCUITEST_DESTINATION='platform=iOS Simulator,id=B66F0347-E40B-406C-AADC-72D95C2E2810'
export AGENT_DEVICE_IOS_CLEAN_DERIVED=0
ad() { node bin/agent-device.mjs "$@" --platform ios --udid B66F0347-E40B-406C-AADC-72D95C2E2810 --session issue3524-r4-native --json; }
# prepare used the same host-lock wrapper above:
ad prepare ios-runner --timeout 240000
ad open com.callstack.issue3524.nativefacts --relaunch --launch-args=--agent-device-native-states
ad snapshot --debug
ad snapshot --actions --debug
ad snapshot --observe-only --debug
ad is editable 'id=native-field'
ad is editable 'id=native-disabled'Prepare reported Producer proof: default request Simulator AX bridge {"type":"TextField","identifier":"native-field","value":"Native input","password":false,"editable":true,"enabled":true}
{"type":"SecureTextField","identifier":"native-secure","value":"Native input","password":true,"editable":true,"enabled":true}
{"type":"TextField","identifier":"native-disabled","value":"Native input","password":false,"editable":true,"enabled":false}
{"type":"Switch","identifier":"native-off","value":"0","checked":false,"enabled":true}
{"type":"Switch","identifier":"native-on","value":"1","checked":true,"enabled":true}
{"type":"StaticText","label":"Agent Device Runner","enabled":true}Apple runner — private AX {"type":"TextField","identifier":"native-field","password":false,"editable":true,"enabled":true}
{"type":"SecureTextField","identifier":"native-secure","password":true,"editable":true,"enabled":true}
{"type":"TextField","identifier":"native-disabled","password":false,"editable":true,"enabled":false}
{"type":"Switch","identifier":"native-off","value":"0","checked":false,"enabled":true}
{"type":"Switch","identifier":"native-on","value":"1","checked":true,"enabled":true}
{"type":"StaticText","label":"Agent Device Runner","enabled":true}Apple runner — recursive XCTest tree {"type":"TextField","identifier":"native-field","password":false,"editable":true,"enabled":true}
{"type":"SecureTextField","identifier":"native-secure","password":true,"editable":true,"enabled":true}
{"type":"TextField","identifier":"native-disabled","password":false,"editable":true,"enabled":false}
{"type":"Switch","identifier":"native-off","label":"0","value":"0","checked":false,"enabled":true}
{"type":"Switch","identifier":"native-on","label":"1","value":"1","checked":true,"enabled":true}
{"type":"StaticText","label":"Agent Device Runner","enabled":true}All four original JSON captures passed assertions for checked false/true, secure password true/editable true, plain password false/editable true, disabled password false/editable true/enabled false, and absence of checked/password/editable on every StaticText node. The projections above omit geometry, refs and containers; key absence was checked in the originals. Validation, cleanup, and review stateNo runtime fixes, commits or pushes were needed. The worktree remains clean at Appium and native sessions were closed; both isolated daemons stopped with # Appium shell environment:
node bin/agent-device.mjs close --platform ios --session issue3524-r4-appium --json
node bin/agent-device.mjs daemon stop --clean --json
# Native shell environment:
ad close
node bin/agent-device.mjs daemon stop --clean --json
xcrun simctl shutdown B66F0347-E40B-406C-AADC-72D95C2E2810
xcrun simctl delete B66F0347-E40B-406C-AADC-72D95C2E2810Simulator deletion was asserted against the final device list. No existing device owner was changed, no merge occurred, and |
|
The PR is ready at 142bb84. The earlier evidence request from fb7e850 is now satisfied, and the code looks right to me. Not blocking: createIosSnapshotAcquisition in packages/capture-kit/src/ios-snapshot-acquisition.ts became async only to lazy-load a 24-line module from the same package, so every caller and test now awaits it. If the budget allows later, a static import would make it sync again. Take it or leave it. Both inline threads from the other reviewer are fixed at this head and can be resolved: the SnapshotModelsTests cases now use independent checked, password and editable values per row (#3592 (comment)), and snapshots.md now names every backend that publishes these states (#3592 (comment)). CI is green: 30 checks, none failing, including iOS smoke and the mutation lanes that cover the changed snapshot route. I did not reproduce the live captures, so they are your posted output. The Limrun capture ran on f2e96e2. The head only adds a docs commit on top, so it adds no new runtime code. The reason for the dynamic import is your statement about the eager-closure gate, and I did not run that gate. I ran no tests myself; I checked the behavior by reading the code. Nothing else stands in the way of a human merge decision. |
|
Merged
|
f27e260 to
ee2017d
Compare
ee2017d to
b1d3035
Compare
Refactor: one iOS node interpretation stepHead:
Deleted the Swift native-state interpreter and golden consumer, Swift checked/password/editable carriers, the separate TS native-state module/export, and the async acquisition ripple. Modal nodes now carry their trimmed native value. Retained reported Android facts, heading/roleDescription mappings, secure Limrun subroles, docs, and the reindexing cleanup (two repeated constructors removed). R74 distinguishes raw acquisition from presentation; assembly still cannot import acquisition policy or the engine, and producers still cannot import the engine. Its planted old bans remain red and the acquisition case passes. Diffstat against main
Net addition is 145 lines smaller (22%). No Regression and local validationThe exact five-producer command changed from 28 failed / 67 passed on pre-fix main implementations ( pnpm exec vitest run --project unit-core --project apple-runner packages/platform-apple/src/runner/__tests__/snapshot-presentation.test.ts packages/platform-apple/src/core/__tests__/runner-selector-query.test.ts packages/platform-apple/src/snapshot-source/tree.test.ts packages/provider-limrun/src/ios-snapshot-adapter.test.ts packages/provider-webdriver/src/webdriver-ios-snapshot.test.tsThe combined golden test replays both native-state and RN tables. Disabling native derivation yields 41 failures; disabling RN interpretation yields 26; restored focused suite: 186 passed. Two independent fresh-context reviews found no issues. On
Live evidenceCreated and used only The Device-panel executable Exact operations through that helper: ad install com.callstack.issue3592.nativefacts /private/tmp/3592-refactor-evidence/NativeStates.app
ad prepare ios-runner --timeout 240000
ad open com.callstack.issue3592.nativefacts --relaunch --launch-args=--agent-device-native-states
ad snapshot --actions --debug
ad snapshot --observe-only --debug
ad snapshot --debug
ad get attrs id=native-off
ad get attrs id=native-on
ad get attrs id=native-field
ad get attrs id=native-secure
ad get attrs id=native-disabled
ad is checked id=native-on
ad is checked id=native-off
ad is editable id=native-field
ad is editable id=native-disabled
ad open com.callstack.issue3592.nativefacts --relaunch --launch-args=--agent-device-native-states --launch-args=--agent-device-alert-replacement-regression --launch-args=--agent-device-alert-banner
ad snapshot --actions --debug
ad alert dismiss
ad closeRunner path proof:
The live notification permission modal captured an Alert titled “AgentDeviceRunner” Would Like to Send You Notifications with Don’t Allow/Allow actions; the actual modal XCTest separately proves preservation of valued controls. Appium 3.8.0 / XCUITest 12.17.1 used a temporary native-appium provider plugin, port 4739, WDA port 8139, derived path ad-appium connect native-appium
ad-appium open com.callstack.issue3592.nativefacts --timeout 240000
ad-appium snapshot --debugThe provider helper keeps config/session/HTTP daemon flags; the provider's capability pins the UDID. Appium logs real Limrun used credentials sourced from the main checkout's .env, an owned uploaded asset and an owned temporary instance, attached by instance URL/token. CLI (source daemon via --experimental-strip-types): Captured JSON excerptsrunner-private-ax [
{
"type": "TextField",
"identifier": "native-field",
"password": false,
"editable": true,
"enabled": true
},
{
"type": "SecureTextField",
"identifier": "native-secure",
"password": true,
"editable": true,
"enabled": true
},
{
"type": "TextField",
"identifier": "native-disabled",
"password": false,
"editable": true,
"enabled": false
},
{
"type": "Switch",
"identifier": "native-off",
"value": "0",
"checked": false,
"enabled": true
},
{
"type": "Switch",
"identifier": "native-on",
"value": "1",
"checked": true,
"enabled": true
},
{
"type": "Other",
"identifier": "rn-checkbox",
"value": "checkbox, unchecked",
"checked": false,
"enabled": true,
"iosValueSemantics": {
"source": "react-native-ios-value-heuristic",
"encoding": "en",
"role": "checkbox",
"checked": false
}
}
]runner-tree [
{
"type": "TextField",
"identifier": "native-field",
"password": false,
"editable": true,
"enabled": true
},
{
"type": "SecureTextField",
"identifier": "native-secure",
"password": true,
"editable": true,
"enabled": true
},
{
"type": "TextField",
"identifier": "native-disabled",
"password": false,
"editable": true,
"enabled": false
},
{
"type": "Switch",
"identifier": "native-off",
"value": "0",
"checked": false,
"enabled": true
},
{
"type": "Switch",
"identifier": "native-on",
"value": "1",
"checked": true,
"enabled": true
},
{
"type": "Other",
"identifier": "rn-checkbox",
"value": "checkbox, unchecked",
"checked": false,
"enabled": true,
"iosValueSemantics": {
"source": "react-native-ios-value-heuristic",
"encoding": "en",
"role": "checkbox",
"checked": false
}
}
]bridge [
{
"type": "TextField",
"identifier": "native-field",
"value": "Native input",
"password": false,
"editable": true,
"enabled": true
},
{
"type": "SecureTextField",
"identifier": "native-secure",
"value": "Native input",
"password": true,
"editable": true,
"enabled": true
},
{
"type": "TextField",
"identifier": "native-disabled",
"value": "Native input",
"password": false,
"editable": true,
"enabled": false
},
{
"type": "Switch",
"identifier": "native-off",
"value": "0",
"checked": false,
"enabled": true
},
{
"type": "Switch",
"identifier": "native-on",
"value": "1",
"checked": true,
"enabled": true
},
{
"type": "Other",
"identifier": "rn-checkbox",
"value": "checkbox, unchecked",
"checked": false,
"enabled": true,
"iosValueSemantics": {
"source": "react-native-ios-value-heuristic",
"encoding": "en",
"role": "checkbox",
"checked": false
}
}
]appium-snapshot [
{
"type": "XCUIElementTypeTextField",
"identifier": "native-field",
"value": "Native input",
"password": false,
"editable": true,
"enabled": true
},
{
"type": "XCUIElementTypeSecureTextField",
"identifier": "native-secure",
"value": "Native input",
"password": true,
"editable": true,
"enabled": true
},
{
"type": "XCUIElementTypeTextField",
"identifier": "native-disabled",
"value": "Native input",
"password": false,
"editable": true,
"enabled": false
},
{
"type": "XCUIElementTypeSwitch",
"identifier": "native-off",
"value": "0",
"checked": false,
"enabled": true
},
{
"type": "XCUIElementTypeSwitch",
"identifier": "native-on",
"value": "1",
"checked": true,
"enabled": true
},
{
"type": "XCUIElementTypeOther",
"identifier": "rn-checkbox",
"value": "checkbox, unchecked",
"checked": false,
"enabled": true,
"iosValueSemantics": {
"source": "react-native-ios-value-heuristic",
"encoding": "en",
"role": "checkbox",
"checked": false
}
}
]limrun-snapshot [
{
"type": "TextField",
"identifier": "native-field",
"value": "Native input",
"password": false,
"editable": true,
"enabled": true
},
{
"type": "TextField",
"identifier": "native-secure",
"value": "Native input",
"password": true,
"editable": true,
"enabled": true
},
{
"type": "TextField",
"identifier": "native-disabled",
"value": "Native input",
"password": false,
"editable": true,
"enabled": false
},
{
"type": "CheckBox",
"identifier": "native-off",
"value": "0",
"checked": false,
"enabled": true
},
{
"type": "CheckBox",
"identifier": "native-on",
"value": "1",
"checked": true,
"enabled": true
},
{
"type": "GenericElement",
"identifier": "rn-checkbox",
"value": "checkbox, unchecked",
"enabled": true
}
]Cleanup and readinessNative and Appium source daemons are stopped; cleanup reports no orphaned claims or pending provider releases. Appium's session expired after its successful capture, then daemon cleanup completed; the Appium server received SIGINT and confirmed no active sessions and HTTP server closed. Limrun session/daemon, instance and asset were deleted/closed. The Device panel was closed, the owned simulator shut down and deleted. No existing device was used. Worktree retained; no merge and no ready-for-human label change. CI and cubic review are pending on this new head; readiness will be checked against exact-head checks, unresolved threads, and newer human comments/reviews. |
|
Follow-up at All required local gates passed, including committed eager-closure/file-size and layering checks. No new live run is needed: runtime semantics are unchanged and prior live evidence is retained. Diff vs main: 28 files, +641/−135. Awaiting CI and cubic review. |
|
This PR is ready at 64573fd. The redesign fixes the earlier concerns, and I found no problems in the production diff. Not blocking: Two threads from the Cubic review no longer apply, so please resolve them: the Swift checked/password/editable carriers and CI is still running: Smoke Tests and five Mutants lanes are pending, and none has failed. Smoke exercises the iOS snapshot route that the runner presentation and bridge interpreters sit on, so this diff overlaps it. The snapshot-occlusion mutant lane covers the contracts keyboard path, which the reordering does not change semantically. There are no conflicts. I did not run the eager-closure, layering or unit gates, so the 9 to 10 closure claim and the passing gate results are your statements. I did not reproduce the live captures. I used the ones from your comment at b1d3035, and I judged them still valid because the runtime from b1d3035 to the head reads as equivalent in the diff. Since the branch went from 11 commits to 4, I reviewed the full production diff at head, plus the b1d3035 to 64573fd move in detail. Before merge, the Smoke Tests and Mutants lanes on 64573fd need to finish green and Cubic needs to re-review this head. After that it is a human merge decision. |
* origin/main: feat(leasing): proxy CLI names each new lease, daemon keeps a caller-chosen lease id (#3479) feat(remote): name the held lease on upload, artifact download and diagnostics requests (#3594) fix(android): record the launched intent owner from the device (#3600) fix(selectors): type rect-requiring targets as addressable (#3608) feat(snapshot): interpret iOS node facts once at producer seams (#3592) refactor(gates): own the client-only replay rule as daemon-process reachability (R82) (#3604)
* origin/main: feat(remote): name the held lease on upload, artifact download and diagnostics requests (#3594) fix(android): record the launched intent owner from the device (#3600) fix(selectors): type rect-requiring targets as addressable (#3608) feat(snapshot): interpret iOS node facts once at producer seams (#3592) refactor(gates): own the client-only replay rule as daemon-process reachability (R82) (#3604)
* origin/main: feat(remote): name the held lease on upload, artifact download and diagnostics requests (#3594) fix(android): record the launched intent owner from the device (#3600) fix(selectors): type rect-requiring targets as addressable (#3608) feat(snapshot): interpret iOS node facts once at producer seams (#3592) refactor(gates): own the client-only replay rule as daemon-process reachability (R82) (#3604)
Summary
One synchronous capture-kit interpreter acquires native checked/password/editable facts and RN value semantics at runner snapshot/quality/querySelector, AX bridge, Limrun, and Appium adapter seams. Explicit facts win; unknown stays absent. Android reported states and provider heading/roleDescription remain.
Rename the #3589 module and its golden-driven test to
ios-node-interpretation. R74 andios-snapshot-acquisition.tsmatch main exactly: importing interpretation into acquisition would grow its eager closure from 9 to 10 modules. Remove duplicated Swift/TS rules and Swift state carriers; modal nodes preserve native values. 28 files; snapshot docs updated.Closes #3524
Validation
Head
64573fd90119870854aee208572ebd61712ca584: focused tests (234), typecheck, lint, repository-wide format, host-lockedpnpm check:affected --run(15,325 related tests), committed eager-closure/file-size checks (1,039), and layering tests (330) passed.Original five-seam regression evidence:
Prior live/native evidence covers runner private AX, recursive XCTest, bridge, Appium, Limrun, and a notification modal; temporary resources were cleaned up. No new live run is needed for this behavior-preserving module move.
is checkedis unavailable; checked facts were verified through snapshots andget attrs. Limrun's GenericElement RN fixture stays uninterpreted.Diff vs main: 28 files, +641/−135. CI and cubic review pending on this head.