feat: direct iOS selector falls back to tree resolution on semantic failures (ADR 0011)#1091
Conversation
Size Report
Startup median (7 runs, lower is better):
Top changed chunks:
|
|
Reviewed by diff (author-gated suites; host busy with the replay suite rerun, CI re-confirms). All three traps from the task spec were handled the right way:
Registry flips and the 6→3 pin shrink match the ADR's hybrid strategy exactly (success-path parity cells untouched). LGTM pending CI. Note for the release: this PR's behavior change (ambiguous/not-found direct-path failures now resolve via runtime instead of erroring) is covered by the react-navigation replay suite currently running against merged main — I'll rerun it against this branch if the maintainer wants belt-and-suspenders before merge. |
|
Coordination note: #1092 (contract-suite coverage gate) should merge first; this PR then rebases and must add three coverage-manifest entries for its newly-delegated cells (errorTaxonomy/nonHittable/occlusion) — its own fallback provider scenarios already prove those behaviors, so it's manifest wiring, not new tests. I'll do the rebase when #1092 lands. |
…ailures (ADR 0011) Runner ELEMENT_NOT_FOUND/AMBIGUOUS_MATCH on the direct iOS selector dispatch now delegate to the tree-based runtime path, which supplies runtime disambiguation, occlusion refusal, non-hittable promotion/annotation, and rich selector diagnostics/hints. Maestro replay dispatches (allowNonHittableCoordinateFallback) keep the runner-native error shapes: the new delegateSemanticFailures option is threaded from the dispatch flag, and the query path (allowElementNotFound at selector-runtime) keeps its exact semantics. Registry: direct-ios-selector errorTaxonomy/nonHittable/occlusion flip from gap waivers to delegated cells; the pinned gap list shrinks 6 -> 3. Success-path parity cells (disambiguation, responseIdentity) intentionally remain gaps per ADR 0011. Refs #1081
|
Sequencing blocker: #1092 has now merged, so this branch needs the promised rebase/update before it is ready. The current #1091 diff still does not include the interaction-contract coverage manifest entries for the newly delegated direct-ios-selector cells (errorTaxonomy/nonHittable/occlusion). Please rebase onto current main and wire those coverage-manifest entries, then rerun the relevant contract/provider checks. |
The coverage gate demanded scenarios the moment the rebase flipped errorTaxonomy/nonHittable/occlusion to delegated — as designed. Three transcript scenarios prove the delegation end to end: runner ELEMENT_NOT_FOUND falls back to runtime no-match diagnostics, to the covered refusal, and to an annotated coordinate tap (the transcript asserts the fallback tap is coordinate-keyed).
f689aec to
97cd231
Compare
|
Re-review status: sequencing blocker resolved. The branch now includes the post-#1092 interaction-contract coverage entries for direct-ios-selector errorTaxonomy, nonHittable, and occlusion, with matching contract scenarios. Focused validation passed locally: pnpm exec vitest run src/contracts/tests/interaction-contract-coverage.test.ts test/integration/interaction-contract/direct-ios-selector.contract.test.ts (9 tests). GitHub checks are green (21/21). Added ready-for-human. |
|
Implements ADR 0011 wave-2 delegation-on-error for the direct iOS selector path. Part of #1081.
What changes
When the direct XCTest runner dispatch fails with a semantic shape, the interaction now falls back to the tree-based runtime path instead of surfacing the bare runner error:
allowNonHittableCoordinateFallback)AMBIGUOUS_MATCHELEMENT_NOT_FOUNDtargetHittable/hint); true misses get the shared no-match diagnosticsELEMENT_OFFSCREENCOMMAND_FAILEDMaestro gating (explicit)
The maestro replay path flows through the same dispatch, so the flag is threaded into the fallback decision explicitly:
isDirectIosSelectorFallbackError(error, { delegateSemanticFailures: selector.allowNonHittableCoordinateFallback !== true }). Replay matching is intentionally runner-native (unique-or-ambiguous scan); its error semantics are byte-for-byte unchanged, and both sides are pinned by unit tests and a provider scenario.Query-path semantics preserved
selector-runtime.ts'squeryDirectIosSelectorOrFallback(wait/get/is) keeps its exact behavior via the untouchedallowElementNotFoundoption:ELEMENT_NOT_FOUNDstill falls back,AMBIGUOUS_MATCHstill surfaces as-is (pinned by the existingget text ... does not snapshot-fallback on ambiguous runner matchtest). The default was not inverted precisely to avoid changing that caller.Registry (ADR 0011 Layer 1)
direct-ios-selectorerrorTaxonomy/nonHittable/occlusionflip fromgap:waivers todelegated → runtime-selectorcells (with the non-maestro nuance in the cell text); the pinned gap list shrinks 6 → 3.Success-path parity cells intentionally remain gaps per the ADR:
disambiguationandresponseIdentitystay waived — delegation-on-error cannot catch the case where XCTest succeeds on a candidate the runtime rules would refuse or rank differently.maestro-non-hittable-fallback/errorTaxonomyalso stays (this PR deliberately preserves those shapes).Tests
direct-ios-selector.test.ts), flipped + new maestro-gated dispatch tests (interaction.test.ts).interaction-direct-selector-fallback.test.ts, transcript-driven): ambiguous → fallback → disambiguation taps the visible twin; ELEMENT_NOT_FOUND on covered element → occlusion refusal + hint; ELEMENT_NOT_FOUND on non-hittable element →targetHittable: false+ hint annotation; maestro dispatch →AMBIGUOUS_MATCHsurfaces with zero fallback traffic (single-entry transcript + wire-level flag assertion).Not done (called out)
The message-sniffing transport checks in
isDirectIosSelectorFallbackErrorstay: the sniffed shapes originate at 4+ scattered throw sites plus raw undicifetch failedTypeErrors that are never AppErrors before this boundary, andisRetryableRunnerErrordoes the same sniffing for retry policy — a typed transport marker needs one coordinated wrapping change, left as Tier-3 cleanup (documented in-code).Verification
format:check,typecheck,lint,fallow audit --base origin/main(clean; one inherited duplication excluded by gate),vitest run src/daemon src/commands src/contracts(1232 passed),vitest run test/integration(93 passed).