Skip to content

test(ios): add a shared AX recovery conformance fixture with a host adapter - #2425

Merged
thymikee merged 1 commit into
fix/ios-ax-depth-recoveryfrom
claude/agent-device-issue-2424-conformance
Sep 9, 2026
Merged

test(ios): add a shared AX recovery conformance fixture with a host adapter#2425
thymikee merged 1 commit into
fix/ios-ax-depth-recoveryfrom
claude/agent-device-issue-2424-conformance

Conversation

@thymikee

@thymikee thymikee commented Sep 9, 2026

Copy link
Copy Markdown
Member

Summary

Adds contracts/fixtures/ios-ax-recovery-conformance.json, a shared executable recovery contract for the host AX bridge and the XCTest runner's private AX bridge. Fifteen recovery cases (569 fixture lines; hint cases arrive with the runner adapter in #2428) cover zero-based depth versus native levels, node budgets, complete/withheld/unknown frontier evidence, missing live elements, owner change, request exhaustion, deadline, explicit raw depth, hinted first requests, and multi-branch recovery. Every expectation names the outcome, the native request accounting, the deepest level, and the delivered tree as a canonical preorder signature with its retained node count, so a producer that drops, duplicates, reorders, or re-parents nodes cannot pass as complete. Expectations are per producer, and the fixture documents the intentional differences instead of averaging them away.

This PR carries the host adapter: an Objective-C driver over captureSnapshotTree, compiled and run per case by native-runtime.test.ts. The runner adapter and the accepted-depth memory characterization follow in #2428, which executes the fixture's runner column and adds the hint cases; README and ADR 0004 mark the runner adapter as pending until then.

First step of #2424, stacked on #2414; no runtime behaviour change. Five files, 903 lines (569 fixture).

Validation

Tested head: d9a3c2afa9.

  • pnpm check:affected --run --base origin/fix/ios-ax-depth-recovery passed.
  • Native: 46 compiled cases (31 existing + 15 fixture cases) pass via native-runtime.test.ts.
  • Planted red in the host materializer, each restored afterwards: keep only the first sibling → multi-branch-recovery, runner-extension-budget red; duplicate the last sibling → 8 cases red (signature and node count); reverse siblings → 2 red. Also: missing boundary child count accepted → 7 red; request budget 33 → 3 red; native levels off by one → 8 red.
  • Review follow-ups: structural signatures and the multi-branch case added (P1); hint characterization moved to test(ios): run the shared AX recovery fixture through the runner and characterize its depth memory #2428 (P2/scope), diff reduced from 1,551 to 903 lines; docs mark the runner adapter as pending; the fixture no longer restates code constants (ladder, budgets, retries) that the cases pin behaviourally.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.52 MB 4.52 MB +965 B
Package (unpacked) 4.52 MB 4.52 MB +965 B
Package (download) 1.34 MB 1.34 MB +359 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 27.1 ms 26.9 ms -0.2 ms
CLI --help 74.8 ms 74.7 ms -0.1 ms

@thymikee

thymikee commented Sep 9, 2026

Copy link
Copy Markdown
Member Author

Reviewed 509bc1c against #2424 and both capture producers. The shared fixture is the right direction, and I found no runtime regression in the production extraction. I would address these before merging:

[P1] Assert the recovered tree, not only its maximum depth

The host assertions and runner assertions check outcome, counts and deepest level, but not which nodes survived or their structure.

Verified counterexample: in a temporary copy of the host materializer, I discarded every sibling except the first after recovery. Every applicable shared host case still passed (11 executed; 3 marked not applicable). A capture can therefore lose branches and still satisfy this conformance suite as “complete.”

Please assert a compact canonical tree signature covering ordered node identities and parent relationships, plus retained node count. Include successful multi-branch recovery and the expected retained tree for bounded captures, with producer-specific expectations where necessary. Prove dropped, duplicated and reordered siblings fail. This is central to #2424's completeness contract.

[P2] Remove or exercise the unused hint-outcome fields

The hint loop reads acceptedLevels and directly invokes the memory helper. Its decoded outcome.complete and outcome.rejected fields are never consumed. Consequently, the complete/bounded learning cases never exercise those capture outcomes; changing either field cannot change the result.

Either drive the real recovery-to-learning transition with these inputs and assert its observations, or delete the unused fields and narrow the case names/claims to direct memory-state tests. Preserve the runner's current behavior of learning a lower accepted rung even from bounded recovery; this characterization PR need not change that policy.

Scope cleanup

The actual diff is 1,551 gross lines (1,512 additions + 39 deletions), above the repository's 1,000-line PR budget. Please reduce unnecessary fixture/schema scaffolding or split the memory characterization into a separate cohesive PR. Keep meaningful recovery coverage rather than compressing JSON just to lower the count.

Validation: compiled and ran the standalone host fixture, then repeated it with the sibling-loss mutation. I did not run the native Swift suite locally. No repository source files were changed.

@thymikee
thymikee force-pushed the claude/agent-device-issue-2424-conformance branch from 509bc1c to 2ac55a1 Compare September 9, 2026 14:38
@thymikee thymikee changed the title test(ios): add a shared AX recovery conformance fixture for both producers test(ios): add a shared AX recovery conformance fixture with a host adapter Sep 9, 2026
@thymikee

thymikee commented Sep 9, 2026

Copy link
Copy Markdown
Member Author

Re-reviewed 2ac55a1b369cdbb0e5456e51e0bfb64c54b396d5: the code-level findings are addressed for this host-only slice.

  • Independently compiled and ran all 12 applicable host cases: green (the other 3 cases are runner-only).
  • Repeated the sibling-loss mutation, plus sibling duplication and reversal: each now fails multi-branch-recovery and runner-extension-budget.
  • The misleading hint tests/fields and runner production extraction are removed from this PR. The runner follow-up needs its own review; this does not yet complete two-producer conformance.
  • Actual diff: 5 files, 904 additions, within the repository budget. No new code blocker found.

One small cleanup from the split: apple/snapshot-bridge/README.md says the runner already replays the cases in RunnerTests+AXRecoveryConformanceTests.swift, and ADR 0004 says each producer replays every case. That adapter is absent at this head. Mark runner execution as pending the stacked follow-up and link it when available. The PR body also still mentions hint cases and a 720-line fixture; this head has no hintCases and the fixture is 571 lines.

CI on this exact head is still running, with no reported failures so far. Code looks ready once CI is green; the above is documentation cleanup, not a new runtime blocker.

@thymikee

thymikee commented Sep 9, 2026

Copy link
Copy Markdown
Member Author

The host-only correction at 2ac55a1 addresses the tree-completeness findings; the smaller scope also reduces packaged growth to 831 B. Please update the README and ADR to mark the runner adapter as pending, as noted above. Merge #2414 first; this slice does not yet complete two-producer conformance. CI on the new head is still running.

@thymikee

thymikee commented Sep 9, 2026

Copy link
Copy Markdown
Member Author

Addressed on 2ac55a1b36; the stack is now three PRs.

P1 (tree structure). Every recovery expectation now carries tree, a canonical preorder signature (identities, <parent for a non-canonical parent, first-last runs per branch), and nodes, the retained count; nativeModel.signature documents the format. Added multi-branch-recovery (three branches recovered through three continuations / three extension calls) and the bounded trees of node-budget, deeper-than-requested, explicit-depth-honored, and runner-extension-budget (eight of twelve frontiers extended) are spelled out per producer. Planted in the host materializer and restored: keep only the first sibling → 2 cases red; duplicate the last sibling → 8 red; reverse siblings → 2 red. Planted in the runner serializer: reverse children → 3 cases red (multi-branch-recovery, host-request-budget, runner-extension-budget). The signature walk is bounded by the bridge's node limit, so a shared or cyclic subtree reads as an oversized signature rather than a hang (the duplicate mutation found that).

P2 (unused hint fields). The runner memory test now drives the real transition: each hint step builds a chain that fits the accepted rung (complete: true) or a longer chain under a node budget of that rung (complete: false), runs the real ladder against a client that rejects above the accepted rung, asserts the observed rejections (rejected.runner), the accepted rung, and boundedness, and only then hands effectiveDepth to the runner's own recording step. rejected became per-producer because the ladders differ. The runner's policy of learning a lower rung from a bounded recovery is preserved and asserted.

Scope. Split by producer: this PR keeps the fixture and the host adapter (904 lines, 720 of them fixture); #2428 carries the runner seam, the Swift adapter, and the memory characterization; #2427 rebases on top and adds the host hint column (its hint cases now run through the source adapter rather than the unit).

…dapter

Add contracts/fixtures/ios-ax-recovery-conformance.json, a shared executable
recovery contract for the host AX bridge and the XCTest runner's private AX
bridge. Every expectation names the outcome, the native request accounting,
and the delivered tree as a canonical preorder signature with its retained
node count, so a producer that drops, duplicates, reorders, or re-parents
nodes cannot pass as complete. The fixture records per-producer expectations
and documents the intentional differences (depth vocabulary, ladders,
frontier evidence, budgets, ownership and deadlines, hint lifetime).

The host adapter is an Objective-C driver over captureSnapshotTree, compiled
and run per case by native-runtime.test.ts. The runner adapter and the
accepted-depth memory characterization follow in a stacked PR.
@thymikee
thymikee force-pushed the claude/agent-device-issue-2424-conformance branch from 2ac55a1 to d9a3c2a Compare September 9, 2026 15:09
@thymikee

thymikee commented Sep 9, 2026

Copy link
Copy Markdown
Member Author

The d9a3c2a cleanup resolves the documentation scope gap without weakening the behavioral fixture assertions. This host-only slice looks clean; merge #2414 first. #2428 supplies the runner adapter later. CI is still running.

@thymikee

thymikee commented Sep 9, 2026

Copy link
Copy Markdown
Member Author

Docs and body reconciled at d9a3c2afa9: README and ADR 0004 now say the runner adapter is pending in #2428 (which restores the present-tense wording when it lands), and the body reflects 15 recovery cases, 569 fixture lines, no hint cases. The fixture also stopped restating code constants (ladder, budgets, retry count) that the cases pin behaviourally. The failed iOS smoke job was re-run; the diff has no runtime change.

@thymikee
thymikee merged commit 4d9e7ff into main Sep 9, 2026
18 checks passed
@thymikee
thymikee deleted the claude/agent-device-issue-2424-conformance branch September 9, 2026 16:24
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-09 16:24 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant