Skip to content

fix(cache): drop the engine version from the replay cache key - #929

Merged
okwasniewski merged 1 commit into
mainfrom
oskar/cache-key-engine-version
Oct 6, 2026
Merged

okwasniewski merged 1 commit into
mainfrom
oskar/cache-key-engine-version

Conversation

@okwasniewski

@okwasniewski okwasniewski commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

What and why

  • Every chore: version packages merge fails the strict web agent job on main with REPLAY_STALE on all 25 replayable steps (chore: version packages #825 now, chore: version packages #774 before it, re-recorded in test(web-benchmark): re-record agent recordings for @e2e-dev/web 0.12 #833). The cache key carried the engine's major.minor, and a 0.x engine bumps its minor on every release.
  • The key now keeps the engine's name and SPI version only. A release of the same engine is caught where it matters: each recorded action re-finds its node against a fresh observation at replay, so a node a new version resolves differently misses then (relocate.ts). Keying on the version only cold-started recordings for engines that had not changed how a node resolves.
  • The 30 web benchmark entries move to their new hashes. Each was re-keyed offline: the old key rebuilt from the entry's recordedFor and the agent config hashed to the committed file name for all 30, then the same parts hashed without the engine version. Entry contents are untouched (git shows 30 renames).
  • The mobile benchmark entries stay as they are. They were recorded on Sep 23-24 under conservative/5 and the pre-fix(cache): stop replays self-finalizing when the effect did not happen #732 key shape, so they have been stale since fix(cache): stop replays self-finalizing when the effect did not happen #732 and need a real re-record on a Mac, not a re-key (mobile CI does not run strict, so it spends model calls instead of failing).
  • Docs: cache.mdx "What must match", writing-an-engine.mdx, the engine reference, and the AGENTS.md note on key changes.

Verified

Ran it locally: yes

  • pnpm --filter @e2e-dev/web-benchmark run test:agent --strict-cache against the built packages with a dummy gateway key, so a step that reaches the model fails and a replayed one cannot be mistaken for a live run:
main (#825 CI) this branch
REPLAY_STALE 25 0
Replayed 0 26
Handed off - 2
Missed - 4
Model calls 8 1 (dummy key, rejected)

The 26/2/4 split is the one #833 recorded. The 7 failures are all MODEL_PROVIDER_FAILED from the dummy key on the handed-off and never-recorded steps; the cache directory was left unchanged by the run.

  • pnpm --filter e2e exec vitest run --project unit: 2459 passed. lint, typecheck, docs:check pass.

Checklist

  • Changeset added (pnpm changeset) for any change to a published package, or the change is testbed, docs, or CI only.
  • Breaking change: title carries !, the changeset body names what breaks and what replaces it, and the deprecation warning landed a window earlier (stability policy in CONTRIBUTING.md).
  • Docs updated in the same PR: the docs/**/*.mdx page for the behavior, and skills/e2e/ if the skill describes it.
  • Contract change: emitted .d.ts reviewed, tests/types/sdk-types.ts updated; wire change edits the schema and both fixtures.
  • Engine contract change (e2e/engine): changesets for e2e, @e2e-dev/web, and @e2e-dev/mobile.

Review in cubic Turn on auto-fix

Every engine minor re-keyed all committed recordings, so each `chore:
version packages` merge failed the strict web agent job with REPLAY_STALE
on main (#825 after #774). The key keeps the engine's name and SPI
version; a release of the same engine is caught at replay, where each
action re-finds its node. The 30 web benchmark entries move to their new
hashes by a re-key verified against the old ones.
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

🟢 e2e web: 91 passed

All 91 tests in 41 files
Test Time
🟢 tests/aria-hidden-flow.e2e.ts · 2 passed 621ms
🟢 aria hidden flow › picks the Pro plan by its visible text and confirms it 464ms
🟢 aria hidden flow › another plan shows the error and keeps the picker open 157ms
🟢 tests/async-states.e2e.ts · 2 passed 7.0s
🟢 async states › reads the code off the transient toast and verifies it once the button enables 2.2s
🟢 async states › a wrong code is rejected after the toast has gone 4.8s
🟢 tests/canvas-only.e2e.ts · 2 passed 318ms
🟢 canvas only › taps square, circle, then diamond at their pixel coordinates 165ms
🟢 canvas only › a shape out of order paints the error and resets progress 153ms
🟢 tests/canvas-whiteboard.e2e.ts · 2 passed 500ms
🟢 canvas whiteboard › drags both shapes into their slots and saves 298ms
🟢 canvas whiteboard › bounces a shape off the wrong slot and refuses to save early 202ms
🟢 tests/cart-totals.e2e.ts · 2 passed 437ms
🟢 cart totals › increasing a quantity updates the line total but the order summary stays stale (planted bug) 238ms
🟢 cart totals › a quantity never drops below one 199ms
🟢 tests/checkout-review.e2e.ts · 1 passed 214ms
🟢 shows a raw greeting token and an undefined delivery date (planted bug), yet places the order 214ms
🟢 tests/context-menu-trap.e2e.ts · 2 passed 694ms
🟢 context menu trap › right-click opens nothing; the row menu renames the file 351ms
🟢 context menu trap › the decoy menu items refuse and keep the file 343ms
🟢 tests/control-inventory.e2e.ts · 8 passed 4.0s
🟢 control inventory › check sets the checkbox and the radio without flipping a set one 297ms
🟢 control inventory › check picks a radio the pick replaces with its summary 216ms
🟢 control inventory › a long press and a double click are told apart from a tap 1.0s
🟢 control inventory › the file menu opens on right-click only and Rename renames the file 318ms
🟢 control inventory › setInputFiles attaches files named from the project root 183ms
🟢 control inventory › the details view is left through the browser history 259ms
🟢 control inventory › scrollIntoView brings the footnote into view 316ms
🟢 control inventory › every exercise done shows the success message 1.4s
🟢 tests/css-content-ui.e2e.ts · 2 passed 495ms
🟢 css content ui › the tile labelled Ship it advances and confirming ships 282ms
🟢 css content ui › a wrong tile shows the CSS-rendered error and keeps the picker 213ms
🟢 tests/date-picker.e2e.ts · 2 passed 643ms
🟢 date picker › navigates three months ahead and books the required date 347ms
🟢 date picker › confirming without the exact date shows the error 296ms
🟢 tests/debounced-search.e2e.ts · 2 passed 2.9s
🟢 debounced search › a decoy one character off is refused 1.4s
🟢 debounced search › picking the exact item adds it to the cart 1.4s
🟢 tests/deferred-dialog.e2e.ts · 1 passed 1.2s
🟢 one submit opens one confirm, even when double-clicked 1.2s
🟢 tests/div-soup.e2e.ts · 2 passed 918ms
🟢 div soup › a wrong code is rejected 439ms
🟢 div soup › the right code grants access after the delete tile fixes a typo 479ms
🟢 tests/drag-and-drop.e2e.ts · 2 passed 774ms
🟢 drag and drop › an out-of-order item resets the dropzone 384ms
🟢 drag and drop › dropping Banana then Cherry passes 390ms
🟢 tests/filter-deep-link.e2e.ts · 2 passed 292ms
🟢 filter deep link › filtering writes the query to the URL 149ms
🟢 filter deep link › opening a shared link restores the filter 143ms
🟢 tests/gift-card-purchase.e2e.ts · 3 passed 633ms
🟢 gift card purchase › refuses to buy without an amount 208ms
🟢 gift card purchase › refuses a recipient without an at sign 206ms
🟢 gift card purchase › sends the gift card 219ms
🟢 tests/hover-menu.e2e.ts · 2 passed 561ms
🟢 hover menu › a decoy leaf is refused 226ms
🟢 hover menu › hovering Account, then Billing, reaches Redeem voucher 335ms
🟢 tests/iframe-form.e2e.ts · 2 passed 706ms
🟢 iframe form › a wrong code is refused by the host page 305ms
🟢 iframe form › the code read from one frame applies through the other 401ms
🟢 tests/image-only-ui.e2e.ts · 3 passed 601ms
🟢 image only ui › the icons have no names, so the bare svg locator is ambiguous 148ms
🟢 image only ui › a wrong icon shows the error image and keeps the icons 201ms
🟢 image only ui › tapping the star, then the heart, verifies the icons 252ms
🟢 tests/infinite-scroll.e2e.ts · 1 passed 3.9s
🟢 scrolls the feed until the Golden Ticket loads, then claims it 3.9s
🟢 tests/kanban-board.e2e.ts · 3 passed 776ms
🟢 kanban board › pointer drags move both cards into the goal layout 394ms
🟢 kanban board › a swipe along a path drags a card as well 149ms
🟢 kanban board › a plain tap moves nothing and the untouched board is rejected 233ms
🟢 tests/login-form.e2e.ts · 4 passed 905ms
🟢 login form › rejects a malformed email 228ms
🟢 login form › rejects a short password 200ms
🟢 login form › rejects an unknown account 202ms
🟢 login form › signs in and logs out 275ms
🟢 tests/lying-labels.e2e.ts · 4 passed 888ms
🟢 lying labels › the button whose accessible name says Pay now is the wrong one 223ms
🟢 lying labels › a text match for Pay now catches the decoy phrase too 164ms
🟢 lying labels › the field whose accessible name says Amount is the wrong one 249ms
🟢 lying labels › visible text drives the payment through 252ms
🟢 tests/native-dialogs.e2e.ts · 3 passed 631ms
🟢 native dialogs › answers the confirm, prompt, and alert chain 216ms
🟢 native dialogs › dismissing the confirm cancels the order 230ms
🟢 native dialogs › a wrong gift code is rejected 185ms
🟢 tests/newsletter-signup.e2e.ts · 1 passed 2.3s
🟢 subscribe renders no feedback and logs a TypeError (planted bug) 2.3s
🟢 tests/onboarding-wizard.e2e.ts · 5 passed 4.2s
🟢 onboarding wizard › back keeps what was typed and the brief comes back with it 240ms
🟢 onboarding wizard › the address step takes the brief through its bare-div dropdown 294ms
🟢 onboarding wizard › the profile step steers the calendar three decades back 1.1s
🟢 onboarding wizard › the preferences step flips the toggles the brief names and leaves the trap off 1.2s
🟢 onboarding wizard › the review step repeats the brief and finishes with the PIN 1.3s
🟢 tests/order-history.e2e.ts · 2 passed 1.4s
🟢 order history › overview and settings render their panels 164ms
🟢 order history › the orders tab never leaves its loading state (planted bug) 1.2s
🟢 tests/otp-auto-advance.e2e.ts · 3 passed 1.4s
🟢 otp auto-advance › focus advances with each digit and the full code verifies 486ms
🟢 otp auto-advance › backspace on an empty box clears and refocuses the previous one 148ms
🟢 otp auto-advance › a wrong code clears every box and starts over 761ms
🟢 tests/overlay-trap.e2e.ts · 1 passed 693ms
🟢 continues only after the banner and the overlay are gone 693ms
🟢 tests/playbook-cleanup.e2e.ts · 2 passed 440ms
🟢 playbook cleanup › deleting the draft removes it from an exact-name search 224ms
🟢 playbook cleanup › cancelling the dialog keeps the draft searchable 216ms
🟢 tests/price-sorting.e2e.ts · 3 passed 371ms
🟢 price sorting › opens in the featured order with sale prices struck through 110ms
🟢 price sorting › low to high sorts by the original price, not the paid one (planted bug) 130ms
🟢 price sorting › high to low is the same wrong order reversed (planted bug) 131ms
🟢 tests/product-reviews.e2e.ts · 1 passed 1.1s
🟢 the running-shoe page lists coffee-grinder reviews (planted bug) 1.1s
🟢 tests/promo-storefront.e2e.ts · 1 passed 1.4s
🟢 buys a deal once the promo chrome is out of the way 1.4s
🟢 tests/rich-text-editor.e2e.ts · 2 passed 410ms
🟢 rich text editor › bolding exactly the required word passes the check 209ms
🟢 rich text editor › bold that spills into the previous word fails the check 201ms
🟢 tests/saved-addresses.e2e.ts · 3 passed 518ms
🟢 saved addresses › adds an address and clears the form 175ms
🟢 saved addresses › ignores an add with an empty field 147ms
🟢 saved addresses › delete shows its toast but keeps the address, on retry too (planted bug) 196ms
🟢 tests/shadow-dom-form.e2e.ts · 2 passed 511ms
🟢 shadow DOM form › rejects a wrong access code 261ms
🟢 shadow DOM form › grants access with the code from the hint inside the root 250ms
🟢 tests/smoke.e2e.ts · 1 passed 344ms
🟢 home lists the scenarios and opens one 344ms
🟢 tests/stale-dom.e2e.ts · 1 passed 2.2s
🟢 taps the moving target three times across rebuilds of the list 2.2s
🟢 tests/sticky-chrome.e2e.ts · 1 passed 170ms
🟢 accepts the terms once the button clears the sticky footer 170ms
🟢 tests/virtualized-table.e2e.ts · 1 passed 1m 5s
🟢 scrolls the windowed table to the Golden Row and claims it 1m 5s
🟢 tests/wishlist.e2e.ts · 2 passed 1.5s
🟢 wishlist › saving toggles the button and the tab badge 232ms
🟢 wishlist › the wishlist tab renders the empty state despite the badge count (planted bug) 1.3s

e2e 0.18.0 · 1m 55s · web · run artifacts

@okwasniewski
okwasniewski added this pull request to the merge queue Oct 6, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 39 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/e2e/tests/unit/trace-identity.test.ts">

<violation number="1" location="packages/e2e/tests/unit/trace-identity.test.ts:66">
P2: This compares `target` with an identical clone, so it no longer guards the PR’s key behavior. Compare otherwise-identical targets carrying different `engineVersion` values and require equal hashes.</violation>
</file>

<file name=".changeset/cache-key-engine-version.md">

<violation number="1" location=".changeset/cache-key-engine-version.md:5">
P2: `--strict-cache` identifies old-key entries only when the built-in file store can list them; custom `cache.store` runs cannot report them as `REPLAY_STALE`. Qualify this as file-store behavior.</violation>
</file>

<file name="docs/writing-an-engine.mdx">

<violation number="1" location="docs/writing-an-engine.mdx:100">
P2: This overstates how replay works: free actions and bare point actions replay without re-finding nodes. Qualify the rationale to node-targeted actions so engine authors do not assume every cached action is re-resolved.</violation>
</file>

<file name="docs/cache.mdx">

<violation number="1" location="docs/cache.mdx:304">
P2: A switch to another engine also makes `no-entry` fail with `REPLAY_STALE` when the store holds the same step under its old key. Add engine switches to these documented causes.</violation>
</file>

Heads up: you’re close to your included review allowance. Set a flex budget so reviews don’t pause.

Turn on auto-fix | Re-trigger cubic

buildTraceCacheKey({ ...parts, target: { ...target, engineVersion: '1.61.9' } }),
),
).toBe(hash);
expect(traceCacheKeyHash(buildTraceCacheKey({ ...parts, target: { ...target } }))).toBe(hash);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This compares target with an identical clone, so it no longer guards the PR’s key behavior. Compare otherwise-identical targets carrying different engineVersion values and require equal hashes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/e2e/tests/unit/trace-identity.test.ts, line 66:

<comment>This compares `target` with an identical clone, so it no longer guards the PR’s key behavior. Compare otherwise-identical targets carrying different `engineVersion` values and require equal hashes.</comment>

<file context>
@@ -71,14 +60,10 @@ describe('traceCacheKeyHash', () => {
-        buildTraceCacheKey({ ...parts, target: { ...target, engineVersion: '1.61.9' } }),
-      ),
-    ).toBe(hash);
+    expect(traceCacheKeyHash(buildTraceCacheKey({ ...parts, target: { ...target } }))).toBe(hash);
   });
 
</file context>

"e2e": patch
---

The replay cache key no longer carries the engine's version, so upgrading `@e2e-dev/web` or `@e2e-dev/mobile` keeps every committed recording. A recording re-finds its nodes at replay, which is where a node an engine release resolves differently is caught. Entries recorded before this release sit under the old keys: a `read-write` run records them again, and `--strict-cache` names them as `REPLAY_STALE` until it does.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: --strict-cache identifies old-key entries only when the built-in file store can list them; custom cache.store runs cannot report them as REPLAY_STALE. Qualify this as file-store behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At .changeset/cache-key-engine-version.md, line 5:

<comment>`--strict-cache` identifies old-key entries only when the built-in file store can list them; custom `cache.store` runs cannot report them as `REPLAY_STALE`. Qualify this as file-store behavior.</comment>

<file context>
@@ -0,0 +1,5 @@
+"e2e": patch
+---
+
+The replay cache key no longer carries the engine's version, so upgrading `@e2e-dev/web` or `@e2e-dev/mobile` keeps every committed recording. A recording re-finds its nodes at replay, which is where a node an engine release resolves differently is caught. Entries recorded before this release sit under the old keys: a `read-write` run records them again, and `--strict-cache` names them as `REPLAY_STALE` until it does.
</file context>

supported action kinds. `locate` answers `screen` queries; the exported
`version` is required: reports name it, and a saved session restores only
under the version that saved it. The replay cache does not key on it, since
a recording re-finds its nodes at replay. With `perform`, also declare

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This overstates how replay works: free actions and bare point actions replay without re-finding nodes. Qualify the rationale to node-targeted actions so engine authors do not assume every cached action is re-resolved.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/writing-an-engine.mdx, line 100:

<comment>This overstates how replay works: free actions and bare point actions replay without re-finding nodes. Qualify the rationale to node-targeted actions so engine authors do not assume every cached action is re-resolved.</comment>

<file context>
@@ -95,9 +95,10 @@ export function hyperdrive(options: { device: string }): EngineHandle {
-supported action kinds. `locate` answers `screen` queries; the exported
+`version` is required: reports name it, and a saved session restores only
+under the version that saved it. The replay cache does not key on it, since
+a recording re-finds its nodes at replay. With `perform`, also declare
+`actions` to list the supported action kinds. `locate` answers `screen` queries; the exported
 `resolveExpression` applies the standard query rules to a semantic tree.
</file context>
Suggested change
a recording re-finds its nodes at replay. With `perform`, also declare
node-targeted actions re-find their nodes at replay. With `perform`, also declare

Comment thread docs/cache.mdx
same step under another key, from before an `e2e` or engine upgrade or a
change to the project id, the app's identity, or the agent's context. The
message names that file; delete it once nothing replays it. A custom
same step under another key, from before an `e2e` upgrade that re-keyed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: A switch to another engine also makes no-entry fail with REPLAY_STALE when the store holds the same step under its old key. Add engine switches to these documented causes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/cache.mdx, line 304:

<comment>A switch to another engine also makes `no-entry` fail with `REPLAY_STALE` when the store holds the same step under its old key. Add engine switches to these documented causes.</comment>

<file context>
@@ -299,9 +301,9 @@ The reasons that fail are the ones that mean the app or the entry changed:
-same step under another key, from before an `e2e` or engine upgrade or a
-change to the project id, the app's identity, or the agent's context. The
-message names that file; delete it once nothing replays it. A custom
+same step under another key, from before an `e2e` upgrade that re-keyed
+entries or a change to the project id, the app's identity, or the agent's
+context. The message names that file; delete it once nothing replays it. A custom
</file context>

Merged via the queue into main with commit e77c17a Oct 6, 2026
23 checks passed
@okwasniewski
okwasniewski deleted the oskar/cache-key-engine-version branch October 6, 2026 13:42
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

🟢 e2e web agent: 33 passed

34 agent steps · 26 replayed from cache · 20 model calls · 76.6k tokens (92% cached) · $0.0036

All 33 tests in 5 files
Test Agent Time
🟢 tests-agent/control-inventory.e2e.ts · 7 passed 7 steps 7.6s
🟢 control inventory › check sets the checkbox and the radio 1 step 656ms
🟢 control inventory › check picks a radio the pick replaces with its summary 1 step 377ms
🟢 control inventory › a long press and a double click are chosen over a plain tap 1 step 947ms
🟢 control inventory › a right-click opens the file menu and Rename renames the file 1 step 2.6s
🟢 control inventory › upload attaches the named project files 1 step 2.4s
🟢 control inventory › back returns from the details view to the inventory 1 step 259ms
🟢 control inventory › scroll_to brings the footnote into view 1 step 355ms
🟢 tests-agent/login-form.e2e.ts · 1 passed 1 step 354ms
🟢 act signs in with the benchmark account 1 step 354ms
🟢 tests-agent/planted-bugs.e2e.ts · 3 passed 4 steps · 8 calls 18.7s
🟢 cart totals: a judgment catches the stale order summary 2 steps · 4 calls 5.4s
🟢 checkout review: a judgment catches the broken copy 1 step · 1 call 1.6s
🟢 wishlist: a judgment catches the lost saved items 1 step · 3 calls 11.7s
🟢 tests-agent/scenarios.e2e.ts · 21 passed 21 steps · 12 calls 2m 30s
🟢 act completes shadow-dom-form 1 step · 3 calls 4.4s
🟢 act completes lying-labels 1 step · 3 calls 4.1s
🟢 act completes iframe-form 1 step · 3 calls 3.9s
🟢 act completes infinite-scroll 1 step 5.3s
🟢 act completes virtualized-table 1 step 1m 42s
🟢 act completes overlay-trap 1 step 848ms
🟢 act completes sticky-chrome 1 step 866ms
🟢 act completes async-states 1 step · 3 calls 6.4s
🟢 act completes debounced-search 1 step 2.3s
🟢 act completes hover-menu 1 step 459ms
🟢 act completes drag-and-drop 1 step 418ms
🟢 act completes kanban-board 1 step 625ms
🟢 act completes context-menu-trap 1 step 2.6s
🟢 act completes stale-dom 1 step 449ms
🟢 act completes otp-auto-advance 1 step 676ms
🟢 act completes date-picker 1 step 828ms
🟢 act completes rich-text-editor 1 step 8.0s
🟢 act completes playbook-cleanup 1 step 571ms
🟢 act completes promo-storefront 1 step 2.3s
🟢 act completes gift-card-purchase 1 step 2.5s
🟢 act filters the list and the URL carries the filter 1 step 227ms
🟢 tests-agent/unique-cache.e2e.ts · 1 passed 1 step 300ms
🟢 act adds an address with a run-unique label 1 step 300ms

e2e 0.18.0 · 2m 58s · web · run artifacts

@github-actions github-actions Bot mentioned this pull request Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

🟢 e2e android: 54 passed, 1 skipped

All 55 tests in 7 files
Test Time
⏭️ tests/controls.e2e.ts · 24 passed, 1 skipped 3m 0s
🟢 control inventory › a label query resolves a labeled text field 28.2s
🟢 control inventory › a labeled field answers name, display value, value reads, fill, and clear 12.9s
🟢 control inventory › a placeholder query and attribute reads find the field 5.9s
🟢 control inventory › a secure field takes a fill and denies value reads 7.1s
🟢 control inventory › press sends a character and Enter through the soft keyboard 7.9s
🟢 control inventory › a field autofocuses on mount and focus() moves the focus 10.9s
🟢 control inventory › toBeFocused follows the focus 9.5s
🟢 control inventory › a switch checks, unchecks, and reports its state 9.7s
🟢 control inventory › checkbox and radio roles answer getByRole, and a checkbox toggles by tap 6.6s
🟢 control inventory › checkbox and radio roles check, uncheck, and report their state 11.4s
🟢 control inventory › a tab role answers getByRole 3.8s
🟢 control inventory › a radio group reports the checked option 8.7s
🟢 control inventory › buttons report disabled, enabled, and a state that arrives later 7.6s
🟢 control inventory › a display: none element is absent rather than hidden, its visible twin attached 6.3s
🟢 control inventory › a header answers its accessible name 3.6s
🟢 control inventory › a tab row answers count, all, texts, filters, positions, and switching 5.3s
🟢 control inventory › a list answers counts, list-form text, reads, and boxes in order 5.1s
⏭️ control inventory › a row contains its texts (skipped: React Native flattens a plain View out of the accessibility tree on both platforms: XCTest and the Android helper report a View with a testID as a leaf beside its children, so filter({ has }) and a query scoped to it match nothing; only Sc…)
🟢 control inventory › a long press lands as one 6.9s
🟢 control inventory › a double tap lands inside the 300 ms window 7.7s
🟢 control inventory › an ambiguous locator fails at once 4.9s
🟢 control inventory › screenshot, restart, and clearState reopen the app on its home list 9.2s
🟢 refusals › verbs a device never declares are refused before the engine 2ms
🟢 refusals › keys the soft keyboard cannot send are refused by the engine 90ms
🟢 refusals › a missing locator fails as not found once its timeout passes 1.1s
🟢 tests/device-fixture.e2e.ts · 8 passed 58.3s
🟢 device fixture › setOrientation rotates the window the app measures 4.7s
🟢 device fixture › home leaves the app, openApp brings it back where it was 6.1s
🟢 device fixture › foregroundApp sees the home screen after home() 4.0s
🟢 device fixture › closeApp then openApp relaunches on the home list 6.1s
🟢 device fixture › foregroundApp after closeApp reports the closed session as APP_NOT_OPEN 4.1s
🟢 device fixture › device.back pops the scenario 4.6s
🟢 device fixture › setNetwork and setAirplaneMode change what the app sees 9.0s
🟢 device fixture › setLocation and clearLocation change what the app reads 19.6s
🟢 tests/device.e2e.ts · 4 passed 36.4s
🟢 permission prompt › allowing the system dialog enables the microphone 6.7s
🟢 permission prompt › a denied prompt recovers once the permission is granted outside 12.8s
🟢 sequential onboarding echoes the exact values 10.3s
🟢 onboarding rejects a malformed email before advancing 6.7s
🟢 tests/flows-lists.e2e.ts · 7 passed 2m 13s
🟢 infinite scroll reaches the target item 18.3s
🟢 product catalog adds exactly two of the right variant 9.8s
🟢 gestures: long-press, then swipe left 8.0s
🟢 gestures: double-tap completes the flow 10.1s
🟢 sticky chrome: accept below the fold, then continue 11.3s
🟢 async states: load, pull to refresh, claim through the toast 12.0s
🟢 huge virtualized list reaches row 512 1m 4s
🟢 tests/flows.e2e.ts · 7 passed 51.2s
🟢 text inputs unlock the submit button 6.3s
🟢 filled text inputs report their values 5.7s
🟢 modal flow confirms through the native alert 8.1s
🟢 bottom tabs act inside the last tab 6.5s
🟢 error recovery retries, then confirms the delete 7.9s
🟢 choice controls place the exact order 8.1s
🟢 debounced search selects the target once results arrive 8.6s
🟢 tests/login-form.e2e.ts · 3 passed 29.7s
🟢 login form › rejects a malformed email 8.7s
🟢 login form › rejects an unknown account 8.3s
🟢 login form › signs in and logs out 12.7s
🟢 tests/smoke.e2e.ts · 1 passed 5.8s
🟢 home lists the scenarios and opens one 5.8s

e2e 0.18.0 · 8m 16s · android-emulator · run artifacts

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

🟢 e2e android agent: 20 passed

24 agent steps · 123 model calls · 694.3k tokens (84% cached) · $0.0439

All 20 tests in 1 file
Test Agent Time
🟢 tests-agent/scenarios.e2e.ts · 20 passed 24 steps · 123 calls 10m 29s
🟢 act completes Login Form 1 step · 4 calls 1m 3s
🟢 act completes Infinite Scroll List 1 step · 3 calls 23.4s
🟢 act completes Modal Flow 1 step · 4 calls 10.9s
🟢 act completes Bottom Tabs 1 step · 3 calls 8.1s
🟢 act completes Text Input Variations 1 step · 5 calls 13.2s
🟢 act completes Broken Accessibility 2 steps · 6 calls 26.9s
🟢 act completes Vision Only 2 steps · 6 calls 29.2s
🟢 act completes Gestures 1 step · 12 calls 45.2s
🟢 act completes Async States 1 step · 12 calls 44.3s
🟢 act completes Debounced Search 1 step · 4 calls 10.9s
🟢 act completes Huge Virtualized List 1 step · 3 calls 1m 15s
🟢 act completes Flattened Registration Form 2 steps · 12 calls 1m 6s
🟢 act completes Flattened Login 2 steps · 7 calls 36.7s
🟢 act completes Sticky Chrome Target 1 step · 4 calls 16.1s
🟢 act completes WebView Accessibility 1 step · 6 calls 17.7s
🟢 act completes Permission Prompt 1 step · 3 calls 9.2s
🟢 act completes Product Catalog 1 step · 6 calls 16.1s
🟢 act completes Error Recovery 1 step · 6 calls 19.3s
🟢 act completes Choice Controls 1 step · 6 calls 28.8s
🟢 act completes Sequential Onboarding 1 step · 11 calls 1m 9s

e2e 0.18.0 · 10m 29s · android-emulator · run artifacts

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

🟢 e2e ios: 52 passed, 1 skipped

All 53 tests in 7 files
Test Time
⏭️ tests/controls.e2e.ts · 23 passed, 1 skipped 4m 44s
🟢 control inventory › a label query resolves a labeled text field 20.2s
🟢 control inventory › a labeled field answers name, display value, value reads, fill, and clear 28.2s
🟢 control inventory › a placeholder query and attribute reads find the field 8.0s
🟢 control inventory › a secure field takes a fill and denies value reads 14.6s
🟢 control inventory › press sends a character and Enter through the soft keyboard 12.1s
🟢 control inventory › a field autofocuses on mount and focus() moves the focus 15.6s
🟢 control inventory › toBeFocused follows the focus 12.7s
🟢 control inventory › a switch checks, unchecks, and reports its state 14.0s
🟢 control inventory › checkbox and radio roles answer getByRole, and a checkbox toggles by tap 12.9s
🟢 control inventory › checkbox and radio roles check, uncheck, and report their state 17.5s
🟢 control inventory › a radio group reports the checked option 13.3s
🟢 control inventory › buttons report disabled, enabled, and a state that arrives later 11.5s
🟢 control inventory › a display: none element is absent rather than hidden, its visible twin attached 12.5s
🟢 control inventory › a header answers its accessible name 12.4s
🟢 control inventory › a tab row answers count, all, texts, filters, positions, and switching 12.1s
🟢 control inventory › a list answers counts, list-form text, reads, and boxes in order 12.8s
⏭️ control inventory › a row contains its texts (skipped: React Native flattens a plain View out of the accessibility tree on both platforms: XCTest and the Android helper report a View with a testID as a leaf beside its children, so filter({ has }) and a query scoped to it match nothing; only Sc…)
🟢 control inventory › a long press lands as one 12.8s
🟢 control inventory › pointer verbs land at the point asked for 16.0s
🟢 control inventory › an ambiguous locator fails at once 7.3s
🟢 control inventory › screenshot, restart, and clearState reopen the app on its home list 15.1s
🟢 refusals › verbs a device never declares are refused before the engine 4ms
🟢 refusals › keys the soft keyboard cannot send are refused by the engine 559ms
🟢 refusals › a missing locator fails as not found once its timeout passes 1.4s
🟢 tests/device-fixture.e2e.ts · 9 passed 2m 10s
🟢 device fixture › setAppearance flips the color scheme the app reads 30.5s
🟢 device fixture › setOrientation rotates the window the app measures 13.4s
🟢 device fixture › the clipboard reads back what was written 10.7s
🟢 device fixture › home leaves the app, openApp brings it back where it was 10.1s
🟢 device fixture › closeApp then openApp relaunches on the home list 13.3s
🟢 device fixture › foregroundApp after closeApp reports the closed session as APP_NOT_OPEN 9.0s
🟢 device fixture › device.back pops the scenario 9.3s
🟢 device fixture › setLocation and clearLocation change what the app reads 17.4s
🟢 device fixture › enrollBiometrics and setBiometrics answer a biometric prompt 16.4s
🟢 tests/device.e2e.ts · 2 passed 1m 10s
🟢 sequential onboarding echoes the exact values 50.6s
🟢 onboarding rejects a malformed email before advancing 19.1s
🟢 tests/flows-lists.e2e.ts · 7 passed 4m 43s
🟢 infinite scroll reaches the target item 40.2s
🟢 product catalog adds exactly two of the right variant 15.0s
🟢 gestures: long-press, then swipe left 12.2s
🟢 sticky chrome: accept below the fold, then continue 13.3s
🟢 async states: load, pull to refresh, claim through the toast 16.7s
🟢 huge virtualized list reaches row 512 2m 48s
🟢 web view coupon form applies the code shown on the page 17.2s
🟢 tests/flows.e2e.ts · 7 passed 1m 23s
🟢 text inputs unlock the submit button 16.9s
🟢 filled text inputs report their values 12.4s
🟢 modal flow confirms through the native alert 11.0s
🟢 bottom tabs act inside the last tab 8.9s
🟢 error recovery retries, then confirms the delete 10.9s
🟢 choice controls place the exact order 10.0s
🟢 debounced search selects the target once results arrive 12.9s
🟢 tests/login-form.e2e.ts · 3 passed 1m 6s
🟢 login form › rejects a malformed email 17.7s
🟢 login form › rejects an unknown account 17.9s
🟢 login form › signs in and logs out 30.4s
🟢 tests/smoke.e2e.ts · 1 passed 17.9s
🟢 home lists the scenarios and opens one 17.9s

e2e 0.18.0 · 8m 4s · ios-simulator · run artifacts

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

🟢 e2e ios agent: 21 passed, 1 skipped

25 agent steps · 136 model calls · 821.5k tokens (81% cached) · $0.0563

All 22 tests in 1 file
Test Agent Time
⏭️ tests-agent/scenarios.e2e.ts · 21 passed, 1 skipped 25 steps · 136 calls 12m 29s
🟢 act completes Login Form 1 step · 8 calls 58.6s
🟢 act completes Infinite Scroll List 1 step · 3 calls 53.3s
🟢 act completes Modal Flow 1 step · 4 calls 17.5s
🟢 act completes Bottom Tabs 1 step · 3 calls 13.2s
🟢 act completes Text Input Variations 1 step · 5 calls 29.4s
🟢 act completes Broken Accessibility 2 steps · 7 calls 41.3s
🟢 act completes Vision Only 2 steps · 6 calls 30.7s
🟢 act completes Gestures 1 step · 10 calls 44.8s
🟢 act completes Async States 1 step · 9 calls 36.2s
🟢 act completes Debounced Search 1 step · 4 calls 16.9s
🟢 act completes Flattened Registration Form 2 steps · 14 calls 1m 30s
🟢 act completes Flattened Login 2 steps · 11 calls 1m 6s
🟢 act completes Sticky Chrome Target 1 step · 8 calls 43.9s
🟢 act completes WebView Accessibility 1 step · 5 calls 26.2s
🟢 act completes Permission Prompt 1 step · 3 calls 19.5s
🟢 act completes Photo Picker 1 step · 4 calls 19.3s
🟢 act completes Product Catalog 1 step · 5 calls 17.5s
🟢 act completes Error Recovery 1 step · 6 calls 17.8s
🟢 act completes Choice Controls 1 step · 11 calls 48.8s
🟢 act completes Stripe PaymentSheet 1 step · 6 calls 35.3s
🟢 act completes Apple Pay 1 step · 4 calls 23.6s
⏭️ act completes Apple Pay Billing Address (skipped: the CI simulator's Apple Pay sheet skips the billing-address step, so the scenario cannot be completed there)

e2e 0.18.0 · 12m 30s · ios-simulator · run artifacts

okwasniewski added a commit that referenced this pull request Oct 9, 2026
… drop orphan web entries

The 39 mobile recordings dated from Sep 23-24, before the Oct 3 replay
policy bump (#732) and the key change in #929, so every CI run missed all
of them and called the model. Both platforms are re-recorded on a Mac
(iPhone 17 Pro on iOS 26.5, Pixel 7 API 36). The Android agentic job runs
with --strict-cache like the web one; iOS stays lenient until the three
recordings named in the README replay on a simulator.

The web benchmark cache kept 30 entries under the pre-#929 key next to
their re-keyed twins; the orphans go.
SudoScientist161 pushed a commit to SudoScientist161/e2e that referenced this pull request Oct 9, 2026
…-army#929)

Every engine minor re-keyed all committed recordings, so each `chore:
version packages` merge failed the strict web agent job with REPLAY_STALE
on main (tester-army#825 after tester-army#774). The key keeps the engine's name and SPI
version; a release of the same engine is caught at replay, where each
action re-finds its node. The 30 web benchmark entries move to their new
hashes by a re-key verified against the old ones.
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