From f23d4e9d3dbc329d6e39768869567acdb3140228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Sat, 10 Oct 2026 16:01:51 +0200 Subject: [PATCH 1/4] refactor: keep test-only code out of production exports Production-unused exports accumulated while check:production-exports reported them as warnings that never failed. Clear the ones with no production or public consumer: - move test support out of production modules: the maestro conformance projection joins its harness under packages/maestro/test/conformance, and test-only helpers move into co-located *.fixtures.ts modules (iOS conformance harness, maestro position wrapper, audio-probe descriptor, the renamed target-classification fixtures) - point tests at source modules instead of facade re-exports that only tests read (IS_PREDICATES, CLOUD_WEBDRIVER_PROVIDERS, runtime factory, runner artifact, iOS snapshot engine members) - drive helpers through their production callers: tests call buildIosInteractiveSnapshotPresentation, compare iosSnapshotComparisonIdentityKey, and resolve wrappers through resolveElementReportedTwice; the test-only wrappers and the duplicate equality predicate are removed, as is an unused runtime scroll snapshot helper Co-Authored-By: Claude Opus 5.5 --- .../src/audio-probe-descriptor.fixtures.ts | 10 +++++ packages/capture-kit/src/index.ts | 1 - .../conformance-harness.fixtures.ts | 37 ++++++++++++++++ .../conformance-harness.ts | 36 +--------------- .../ios-snapshot-engine/conformance.test.ts | 2 +- .../src/ios-snapshot-engine/engine.test.ts | 6 +-- .../src/ios-snapshot-engine/index.ts | 7 +-- .../src/ios-snapshot-engine/noise.test.ts | 2 +- .../ios-snapshot-engine/presentation.test.ts | 4 +- .../src/ios-snapshot-engine/semantic-index.ts | 4 -- .../ios-snapshot-engine/transitions.test.ts | 4 +- .../src/ios-snapshot-engine/tree.test.ts | 2 +- .../src/ios-snapshot-engine/web.test.ts | 32 ++++++++++---- .../src/ios-snapshot-planning.test.ts | 33 +++++++++----- .../capture-kit/src/ios-snapshot-planning.ts | 43 ------------------- .../__tests__/snapshot-visibility-ios.test.ts | 4 +- .../ios/action-shelf.test.ts | 12 ++++-- .../snapshot-presentation/ios/rows.test.ts | 6 +-- .../__tests__/runtime-target-position.test.ts | 7 +-- .../runtime-target-ranking.fixtures.ts | 16 +++++++ .../src/internal/runtime-target-ranking.ts | 11 ----- .../conformance}/conformance-normalize.ts | 4 +- .../conformance-selector-projection.test.ts | 6 +-- .../conformance-selector-projection.ts | 2 +- .../conformance-value-coercion.ts | 0 packages/maestro/test/conformance/harness.ts | 2 +- .../maestro/test/conformance/layer2-tree.ts | 6 +-- .../runner-session-close-prep-fence.test.ts | 3 +- .../__tests__/runner-start-admission.test.ts | 2 +- .../src/runner/runner-xctestrun.ts | 1 - .../platform-apple/src/snapshot-route.test.ts | 29 +++++++------ packages/provider-webdriver/src/index.ts | 1 - .../__tests__/target-classification.test.ts | 4 +- ...s.ts => target-classification.fixtures.ts} | 0 packages/selectors/src/index.ts | 1 - .../src/interaction-targeting.test.ts | 10 ++--- .../selectors/src/interaction-targeting.ts | 7 --- scripts/ios-snapshot-differential.test.ts | 6 ++- ...pshot-assembly-presentation-policy.test.ts | 2 +- scripts/maestro-conformance/README.md | 8 ++-- .../is-argument-surface-parity.test.ts | 8 +--- src/__tests__/runtime-public.test.ts | 6 ++- .../__tests__/settle-device-fixtures.ts | 7 +-- .../runtime/__tests__/test-utils/index.ts | 27 +----------- .../interaction/runtime/interactions.test.ts | 7 +-- .../interaction/runtime/selector-is.test.ts | 7 +-- .../interaction/runtime/selector-read.test.ts | 2 +- .../selector-wait-cancellation.test.ts | 7 +-- .../settle-transition-baseline.test.ts | 7 +-- .../interaction/runtime/settle.test.ts | 7 +-- .../runtime/stable-capture.test.ts | 7 +-- .../interaction/runtime/wait-absent.test.ts | 7 +-- .../interaction/runtime/wait-selector.test.ts | 7 +-- .../interaction/runtime/wait-stable.test.ts | 7 +-- .../management/runtime/admin-router.test.ts | 7 +-- src/commands/management/runtime/apps.test.ts | 7 +-- .../runtime/diagnostics-router.test.ts | 4 +- .../recording/runtime/contact-sheet.test.ts | 7 +-- .../recording/runtime/recording.test.ts | 7 +-- .../ios-snapshot-presentation-once.test.ts | 8 ++-- .../audio-probe-resource-recovery.test.ts | 5 +-- .../snapshot-presentation-transitions.test.ts | 15 ++++--- .../snapshot-publication-membership.test.ts | 8 +++- ...session-close-cwd-scoped-resources.test.ts | 9 ++-- .../session-close-resource-cleanup.test.ts | 9 ++-- .../internal/__tests__/session-audio.test.ts | 9 ++-- src/runtime.ts | 6 +-- .../interaction-contract/runtime-harness.ts | 7 +-- .../cloud-webdriver-ios-text-entry.test.ts | 6 +-- .../cloud-webdriver-lease-http.test.ts | 6 +-- .../cloud-webdriver-provider-adapters.test.ts | 2 +- ...oud-webdriver-provider-regressions.test.ts | 2 +- .../cloud-webdriver-runtime.test.ts | 6 +-- .../stale-provider-runtime-admission.test.ts | 6 +-- vitest.config.ts | 3 ++ 75 files changed, 264 insertions(+), 361 deletions(-) create mode 100644 packages/capture-kit/src/audio-probe-descriptor.fixtures.ts create mode 100644 packages/capture-kit/src/ios-snapshot-engine/conformance-harness.fixtures.ts create mode 100644 packages/maestro/src/internal/runtime-target-ranking.fixtures.ts rename packages/maestro/{src/internal => test/conformance}/conformance-normalize.ts (99%) rename packages/maestro/{src/internal/__tests__ => test/conformance}/conformance-selector-projection.test.ts (96%) rename packages/maestro/{src/internal => test/conformance}/conformance-selector-projection.ts (97%) rename packages/maestro/{src/internal => test/conformance}/conformance-value-coercion.ts (100%) rename packages/replay-port/src/{target-classification-fixtures.ts => target-classification.fixtures.ts} (100%) diff --git a/packages/capture-kit/src/audio-probe-descriptor.fixtures.ts b/packages/capture-kit/src/audio-probe-descriptor.fixtures.ts new file mode 100644 index 0000000000..8e3b7b9e05 --- /dev/null +++ b/packages/capture-kit/src/audio-probe-descriptor.fixtures.ts @@ -0,0 +1,10 @@ +import { + hostAudioProbeDescriptorCodec, + type HostAudioProbeDescriptor, +} from './audio-probe-descriptor.ts'; +import { encodeDurableDescriptor } from './durable-resource-envelope.ts'; + +/** The descriptor a live host audio probe persists, for seeding a record an earlier daemon left. */ +export function encodeHostAudioProbeDescriptor(descriptor: HostAudioProbeDescriptor) { + return encodeDurableDescriptor(hostAudioProbeDescriptorCodec, descriptor); +} diff --git a/packages/capture-kit/src/index.ts b/packages/capture-kit/src/index.ts index 3bd6e25cce..e5cacd3ab2 100644 --- a/packages/capture-kit/src/index.ts +++ b/packages/capture-kit/src/index.ts @@ -10,7 +10,6 @@ export { } from './app-log-runtime.ts'; export { createAppLogLiveHandle, createAppLogLiveHandleFromFinish } from './app-log-live-handle.ts'; export { createHostAudioProbeCaptureOperations } from './audio-probe-runtime.ts'; -export { hostAudioProbeDescriptorCodec } from './audio-probe-descriptor.ts'; export { decodeDurableDescriptor } from './durable-descriptor-codec.ts'; export { createScreenRecordingLiveHandle } from './screen-recording-live-handle.ts'; export { createScreenRecordingCompletion } from './screen-recording-completion.ts'; diff --git a/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.fixtures.ts b/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.fixtures.ts new file mode 100644 index 0000000000..a68680a94a --- /dev/null +++ b/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.fixtures.ts @@ -0,0 +1,37 @@ +import { execFileSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; +import { mkdtempForTestSync } from '../tmp-dir.fixtures.ts'; +import type { DifferentialCase } from './conformance-harness.ts'; + +export function swiftToolchainAvailable(): boolean { + if (process.platform !== 'darwin') return false; + /* c8 ignore start */ + try { + execFileSync('swift', ['--version'], { stdio: 'ignore' }); + return true; + } catch { + return false; + } + /* c8 ignore stop */ +} + +export function writeDifferentialFailureArtifact(input: { + testCase: DifferentialCase; + seed: number; + counterexamplePath: string; +}): { directory: string; casePath: string; replayCommand: string } { + const directory = mkdtempForTestSync('ios-snapshot-fuzz-'); + const casePath = path.join(directory, 'case.json'); + fs.writeFileSync(casePath, JSON.stringify({ cases: [input.testCase] }, null, 2) + '\n'); + const replayCommand = [ + 'node --experimental-strip-types', + 'packages/capture-kit/src/ios-snapshot-engine/replay.ts', + JSON.stringify(casePath), + ].join(' '); + fs.writeFileSync( + path.join(directory, 'replay-command.txt'), + replayCommand + '\nseed=' + String(input.seed) + '\npath=' + input.counterexamplePath + '\n', + ); + return { directory, casePath, replayCommand }; +} diff --git a/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.ts b/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.ts index 7c5320dc60..77afbf4647 100644 --- a/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.ts +++ b/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.ts @@ -1,6 +1,4 @@ import { execFileSync } from 'node:child_process'; -import fs from 'node:fs'; -import os from 'node:os'; import path from 'node:path'; import type { IosSnapshotAcquisition } from '@agent-device/contracts/ios-snapshot'; import { @@ -18,7 +16,7 @@ export const SWIFT_RUN_TIMEOUT_MS = 60_000; let swiftHarnessExecutable: string | undefined; -type DifferentialCase = Readonly<{ +export type DifferentialCase = Readonly<{ name: string; route: 'acquired' | 'runner-presented'; projection: 'regular' | 'raw'; @@ -61,18 +59,6 @@ type DifferentialMismatch = Readonly<{ typescript: unknown; }>; -export function swiftToolchainAvailable(): boolean { - if (process.platform !== 'darwin') return false; - /* c8 ignore start */ - try { - execFileSync('swift', ['--version'], { stdio: 'ignore' }); - return true; - } catch { - return false; - } - /* c8 ignore stop */ -} - /* c8 ignore start */ export function compareDifferentialCases( cases: readonly DifferentialCase[], @@ -376,23 +362,3 @@ export function canonicalNodes(nodes: readonly RawSnapshotNode[]): CanonicalNode function canonicalRect(rect: Rect): Rect { return { x: rect.x, y: rect.y, width: rect.width, height: rect.height }; } - -export function writeDifferentialFailureArtifact(input: { - testCase: DifferentialCase; - seed: number; - counterexamplePath: string; -}): { directory: string; casePath: string; replayCommand: string } { - const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'ios-snapshot-fuzz-')); - const casePath = path.join(directory, 'case.json'); - fs.writeFileSync(casePath, JSON.stringify({ cases: [input.testCase] }, null, 2) + '\n'); - const replayCommand = [ - 'node --experimental-strip-types', - 'packages/capture-kit/src/ios-snapshot-engine/replay.ts', - JSON.stringify(casePath), - ].join(' '); - fs.writeFileSync( - path.join(directory, 'replay-command.txt'), - replayCommand + '\nseed=' + String(input.seed) + '\npath=' + input.counterexamplePath + '\n', - ); - return { directory, casePath, replayCommand }; -} diff --git a/packages/capture-kit/src/ios-snapshot-engine/conformance.test.ts b/packages/capture-kit/src/ios-snapshot-engine/conformance.test.ts index 1a7e6a717b..cd4b4b858d 100644 --- a/packages/capture-kit/src/ios-snapshot-engine/conformance.test.ts +++ b/packages/capture-kit/src/ios-snapshot-engine/conformance.test.ts @@ -7,8 +7,8 @@ import { canonicalNodes, runTypeScriptCase, runnerPresentationAgrees, - writeDifferentialFailureArtifact, } from './conformance-harness.ts'; +import { writeDifferentialFailureArtifact } from './conformance-harness.fixtures.ts'; import { acquisitionForGoldenCase, normalizeGoldenNodes, diff --git a/packages/capture-kit/src/ios-snapshot-engine/engine.test.ts b/packages/capture-kit/src/ios-snapshot-engine/engine.test.ts index 07f696f4e1..86ed6bcbb7 100644 --- a/packages/capture-kit/src/ios-snapshot-engine/engine.test.ts +++ b/packages/capture-kit/src/ios-snapshot-engine/engine.test.ts @@ -15,7 +15,7 @@ import { import { toIosSnapshotEngineErrorDetails } from './types.ts'; import { IosSnapshotEngineError, - presentIosInteractiveSnapshot, + buildIosInteractiveSnapshotPresentation, presentIosSnapshot, publishIosSnapshot, } from './index.ts'; @@ -342,13 +342,13 @@ test('a source-declared disabled node is not actionable without hittability evid test('interactive compaction stays available through the engine boundary', () => { const rowRect = { x: 16, y: 80, width: 288, height: 52 }; - const compacted = presentIosInteractiveSnapshot([ + const compacted = buildIosInteractiveSnapshotPresentation([ node(0, 'Application', 'App', viewport), node(1, 'Table', 'Settings', { x: 0, y: 40, width: 320, height: 200 }, 0), node(2, 'Cell', 'General', rowRect, 1, 2), node(3, 'Button', 'General', rowRect, 2, 3), node(4, 'StaticText', 'General', rowRect, 3, 4), - ]); + ]).nodes; assert.deepEqual( compacted.map((entry) => entry.type), diff --git a/packages/capture-kit/src/ios-snapshot-engine/index.ts b/packages/capture-kit/src/ios-snapshot-engine/index.ts index e433ba74ce..1d17095ec1 100644 --- a/packages/capture-kit/src/ios-snapshot-engine/index.ts +++ b/packages/capture-kit/src/ios-snapshot-engine/index.ts @@ -1,10 +1,5 @@ export { presentIosSnapshot, publishIosSnapshot } from './engine.ts'; export { presentIosRunnerSnapshot } from './runner-presentation.ts'; -export { - buildIosInteractiveSnapshotPresentation, - presentIosInteractiveSnapshot, -} from './semantic-index.ts'; -export { collectIosStructuralIdentifierSuppression } from './noise-structural.ts'; -export { mergeReplacement, updateReplacement } from './tree.ts'; +export { buildIosInteractiveSnapshotPresentation } from './semantic-index.ts'; export { IosSnapshotEngineError, toIosSnapshotEngineErrorDetails } from './types.ts'; export type { SnapshotTreeRuleContext } from './tree.ts'; diff --git a/packages/capture-kit/src/ios-snapshot-engine/noise.test.ts b/packages/capture-kit/src/ios-snapshot-engine/noise.test.ts index 42c375adfc..5cc1074b9d 100644 --- a/packages/capture-kit/src/ios-snapshot-engine/noise.test.ts +++ b/packages/capture-kit/src/ios-snapshot-engine/noise.test.ts @@ -2,8 +2,8 @@ import { describe, expect, test } from 'vitest'; import type { RawSnapshotNode } from '@agent-device/kernel/snapshot'; -import { collectIosStructuralIdentifierSuppression } from '@agent-device/capture-kit/ios-snapshot-engine'; import type { SnapshotTreeRuleContext } from '@agent-device/capture-kit/ios-snapshot-engine'; +import { collectIosStructuralIdentifierSuppression } from './noise-structural.ts'; type ReadCounter = { reads: number }; diff --git a/packages/capture-kit/src/ios-snapshot-engine/presentation.test.ts b/packages/capture-kit/src/ios-snapshot-engine/presentation.test.ts index 0eed84e345..d7d8ac9bd8 100644 --- a/packages/capture-kit/src/ios-snapshot-engine/presentation.test.ts +++ b/packages/capture-kit/src/ios-snapshot-engine/presentation.test.ts @@ -1,10 +1,10 @@ import { expect, test } from 'vitest'; import { attachRefs, type RawSnapshotNode } from '@agent-device/kernel/snapshot'; -import { presentIosInteractiveSnapshot } from '@agent-device/capture-kit/ios-snapshot-engine'; +import { buildIosInteractiveSnapshotPresentation } from '@agent-device/capture-kit/ios-snapshot-engine'; function buildSnapshotState(data: { nodes?: RawSnapshotNode[]; backend?: 'xctest' }) { return { - nodes: attachRefs(presentIosInteractiveSnapshot(data.nodes ?? [])), + nodes: attachRefs(buildIosInteractiveSnapshotPresentation(data.nodes ?? []).nodes), backend: data.backend, createdAt: Date.now(), }; diff --git a/packages/capture-kit/src/ios-snapshot-engine/semantic-index.ts b/packages/capture-kit/src/ios-snapshot-engine/semantic-index.ts index 615428b153..7f70866f1c 100644 --- a/packages/capture-kit/src/ios-snapshot-engine/semantic-index.ts +++ b/packages/capture-kit/src/ios-snapshot-engine/semantic-index.ts @@ -17,10 +17,6 @@ const IOS_PRESENTATION_RULES: Array< collectIosPresentationNoiseSuppression, ]; -export function presentIosInteractiveSnapshot(nodes: RawSnapshotNode[]): RawSnapshotNode[] { - return buildIosInteractiveSnapshotPresentation(nodes).nodes; -} - export type IosInteractiveSnapshotPresentation = { nodes: RawSnapshotNode[]; /** Canonical presented representatives for every semantic source index; pure noise maps to []. */ diff --git a/packages/capture-kit/src/ios-snapshot-engine/transitions.test.ts b/packages/capture-kit/src/ios-snapshot-engine/transitions.test.ts index 22522371a2..35dae24817 100644 --- a/packages/capture-kit/src/ios-snapshot-engine/transitions.test.ts +++ b/packages/capture-kit/src/ios-snapshot-engine/transitions.test.ts @@ -1,6 +1,6 @@ import { expect, test } from 'vitest'; import type { RawSnapshotNode } from '@agent-device/kernel/snapshot'; -import { presentIosInteractiveSnapshot } from '@agent-device/capture-kit/ios-snapshot-engine'; +import { buildIosInteractiveSnapshotPresentation } from '@agent-device/capture-kit/ios-snapshot-engine'; test('a disabled navigation title field is promoted to a Button without its hittability', () => { const nodes: RawSnapshotNode[] = [ @@ -49,7 +49,7 @@ test('a disabled navigation title field is promoted to a Button without its hitt }, ]; - const presented = presentIosInteractiveSnapshot(nodes); + const presented = buildIosInteractiveSnapshotPresentation(nodes).nodes; const affordance = presented.find((node) => node.identifier === 'DisplayNameTextField'); expect(affordance).toMatchObject({ type: 'Button', label: 'Team Standup', enabled: true }); diff --git a/packages/capture-kit/src/ios-snapshot-engine/tree.test.ts b/packages/capture-kit/src/ios-snapshot-engine/tree.test.ts index 6a21a561dd..2b8bd19d0b 100644 --- a/packages/capture-kit/src/ios-snapshot-engine/tree.test.ts +++ b/packages/capture-kit/src/ios-snapshot-engine/tree.test.ts @@ -1,6 +1,6 @@ import { expect, test } from 'vitest'; import type { RawSnapshotNode } from '@agent-device/kernel/snapshot'; -import { mergeReplacement, updateReplacement } from '@agent-device/capture-kit/ios-snapshot-engine'; +import { mergeReplacement, updateReplacement } from './tree.ts'; test('replacement updates derive patches from the composed node', () => { const node: RawSnapshotNode = { index: 1, type: 'Table', hiddenContentBelow: true }; diff --git a/packages/capture-kit/src/ios-snapshot-engine/web.test.ts b/packages/capture-kit/src/ios-snapshot-engine/web.test.ts index f742a4044d..90b488507c 100644 --- a/packages/capture-kit/src/ios-snapshot-engine/web.test.ts +++ b/packages/capture-kit/src/ios-snapshot-engine/web.test.ts @@ -1,6 +1,6 @@ import { expect, test } from 'vitest'; import type { RawSnapshotNode } from '@agent-device/kernel/snapshot'; -import { presentIosInteractiveSnapshot } from '@agent-device/capture-kit/ios-snapshot-engine'; +import { buildIosInteractiveSnapshotPresentation } from '@agent-device/capture-kit/ios-snapshot-engine'; test('projects iOS WebKit heading and text wrappers to semantic roles', () => { const nodes: RawSnapshotNode[] = [ @@ -15,7 +15,7 @@ test('projects iOS WebKit heading and text wrappers to semantic roles', () => { ]; expect( - presentIosInteractiveSnapshot(nodes).map((node) => [ + buildIosInteractiveSnapshotPresentation(nodes).nodes.map((node) => [ node.type, node.label, node.value, @@ -38,7 +38,9 @@ test('recognizes numeric WebView types from retained older runners', () => { { index: 2, depth: 2, parentIndex: 1, type: 'StaticText', label: 'Body copy' }, ]; - expect(presentIosInteractiveSnapshot(nodes).map((node) => [node.type, node.label])).toEqual([ + expect( + buildIosInteractiveSnapshotPresentation(nodes).nodes.map((node) => [node.type, node.label]), + ).toEqual([ ['WebView', 'Example page'], ['StaticText', 'Body copy'], ]); @@ -52,7 +54,9 @@ test('keeps a nested heading whose label matches the WebView title', () => { { index: 3, depth: 3, parentIndex: 2, type: 'StaticText', label: 'Same title' }, ]; - expect(presentIosInteractiveSnapshot(nodes).map((node) => [node.type, node.label])).toEqual([ + expect( + buildIosInteractiveSnapshotPresentation(nodes).nodes.map((node) => [node.type, node.label]), + ).toEqual([ ['WebView', 'Same title'], ['Heading', 'Same title'], ]); @@ -68,7 +72,9 @@ test('keeps a heading through repeated WebKit document-title wrappers', () => { { index: 5, depth: 5, parentIndex: 4, type: 'StaticText', label: 'Same title' }, ]; - expect(presentIosInteractiveSnapshot(nodes).map((node) => [node.type, node.label])).toEqual([ + expect( + buildIosInteractiveSnapshotPresentation(nodes).nodes.map((node) => [node.type, node.label]), + ).toEqual([ ['WebView', 'Same title'], ['Other', 'Same title'], ['Other', 'main'], @@ -83,7 +89,9 @@ test('keeps a direct heading whose label matches the WebView title', () => { { index: 2, depth: 2, parentIndex: 1, type: 'StaticText', label: 'Same title' }, ]; - expect(presentIosInteractiveSnapshot(nodes).map((node) => [node.type, node.label])).toEqual([ + expect( + buildIosInteractiveSnapshotPresentation(nodes).nodes.map((node) => [node.type, node.label]), + ).toEqual([ ['WebView', 'Same title'], ['Heading', 'Same title'], ]); @@ -97,7 +105,9 @@ test('does not infer text from a nested same-label descendant', () => { { index: 3, depth: 3, parentIndex: 2, type: 'StaticText', label: 'Container' }, ]; - expect(presentIosInteractiveSnapshot(nodes).map((node) => [node.type, node.label])).toEqual([ + expect( + buildIosInteractiveSnapshotPresentation(nodes).nodes.map((node) => [node.type, node.label]), + ).toEqual([ ['WebView', 'Example'], ['Other', 'Container'], ['Button', 'Action'], @@ -112,7 +122,9 @@ test('keeps a labelled WebKit container with additional semantic content', () => { index: 3, depth: 2, parentIndex: 1, type: 'Link', label: 'Read more' }, ]; - expect(presentIosInteractiveSnapshot(nodes).map((node) => [node.type, node.label])).toEqual([ + expect( + buildIosInteractiveSnapshotPresentation(nodes).nodes.map((node) => [node.type, node.label]), + ).toEqual([ ['WebView', 'Example'], ['Other', 'News'], ['Link', 'Read more'], @@ -126,7 +138,9 @@ test('does not infer semantic roles for matching wrappers outside a WebView', () { index: 2, depth: 2, parentIndex: 1, type: 'StaticText', label: 'Native copy' }, ]; - expect(presentIosInteractiveSnapshot(nodes).map((node) => [node.type, node.label])).toEqual([ + expect( + buildIosInteractiveSnapshotPresentation(nodes).nodes.map((node) => [node.type, node.label]), + ).toEqual([ ['Application', 'Example'], ['Other', 'Native copy'], ]); diff --git a/packages/capture-kit/src/ios-snapshot-planning.test.ts b/packages/capture-kit/src/ios-snapshot-planning.test.ts index 99b4e29731..ab4f36d0c3 100644 --- a/packages/capture-kit/src/ios-snapshot-planning.test.ts +++ b/packages/capture-kit/src/ios-snapshot-planning.test.ts @@ -11,7 +11,7 @@ import type { IosSnapshotRequestInput, } from '@agent-device/contracts/ios-snapshot'; import { - areIosSnapshotComparisonIdentitiesEqual, + iosSnapshotComparisonIdentityKey, buildIosSnapshotComparisonIdentity, buildIosSnapshotPresentationKey, createIosSnapshotRequest, @@ -119,22 +119,33 @@ test('comparison identity rejects every identity axis and residue mismatch', () { ...base, presentationKey: { ...base.presentationKey, depth: 1 } }, { ...base, residue: [{ kind: 'truncated' }] }, ]; - assert.equal(areIosSnapshotComparisonIdentitiesEqual(base, { ...base }), true); assert.equal( - areIosSnapshotComparisonIdentitiesEqual(base, { - ...base, - residue: [{ kind: 'provider-pruned', fields: ['scope', 'nodes'] }], - }), + iosSnapshotComparisonIdentityKey(base) === iosSnapshotComparisonIdentityKey({ ...base }), + true, + ); + assert.equal( + iosSnapshotComparisonIdentityKey(base) === + iosSnapshotComparisonIdentityKey({ + ...base, + residue: [{ kind: 'provider-pruned', fields: ['scope', 'nodes'] }], + }), false, ); for (const mismatch of mismatches) { - assert.equal(areIosSnapshotComparisonIdentitiesEqual(base, mismatch), false); + assert.equal( + iosSnapshotComparisonIdentityKey(base) === iosSnapshotComparisonIdentityKey(mismatch), + false, + ); } assert.equal( - areIosSnapshotComparisonIdentitiesEqual( - { ...base, residue: [{ kind: 'provider-pruned', fields: ['scope', 'nodes'] }] }, - { ...base, residue: [{ kind: 'provider-pruned', fields: ['nodes', 'scope'] }] }, - ), + iosSnapshotComparisonIdentityKey({ + ...base, + residue: [{ kind: 'provider-pruned', fields: ['scope', 'nodes'] }], + }) === + iosSnapshotComparisonIdentityKey({ + ...base, + residue: [{ kind: 'provider-pruned', fields: ['nodes', 'scope'] }], + }), true, ); }); diff --git a/packages/capture-kit/src/ios-snapshot-planning.ts b/packages/capture-kit/src/ios-snapshot-planning.ts index dc047a82c7..dc72bda239 100644 --- a/packages/capture-kit/src/ios-snapshot-planning.ts +++ b/packages/capture-kit/src/ios-snapshot-planning.ts @@ -44,19 +44,6 @@ export function deriveIosCaptureHint(request: IosSnapshotRequest): CaptureHint { }); } -export function areIosSnapshotComparisonIdentitiesEqual( - left: IosSnapshotComparisonIdentity, - right: IosSnapshotComparisonIdentity, -): boolean { - return ( - left.producer === right.producer && - left.intent === right.intent && - lineagesEqual(left.lineage, right.lineage) && - presentationKeysEqual(left.presentationKey, right.presentationKey) && - residuesEqual(left.residue, right.residue) - ); -} - export function iosSnapshotComparisonIdentityKey(identity: IosSnapshotComparisonIdentity): string { return JSON.stringify({ producer: identity.producer, @@ -89,36 +76,6 @@ export function buildIosSnapshotComparisonIdentity( }); } -function lineagesEqual( - left: IosSnapshotComparisonIdentity['lineage'], - right: IosSnapshotComparisonIdentity['lineage'], -): boolean { - return left.targetId === right.targetId && left.generation === right.generation; -} - -function presentationKeysEqual( - left: IosSnapshotPresentationKey, - right: IosSnapshotPresentationKey, -): boolean { - return ( - left.projection === right.projection && - left.interactiveOnly === right.interactiveOnly && - left.depth === right.depth && - left.scope === right.scope && - left.customActions === right.customActions - ); -} - -function residuesEqual( - left: readonly IosAcquisitionResidue[], - right: readonly IosAcquisitionResidue[], -): boolean { - return ( - left.map(residueIdentity).sort().join('\u0000') === - right.map(residueIdentity).sort().join('\u0000') - ); -} - function residueIdentity(residue: IosAcquisitionResidue): string { switch (residue.kind) { case 'provider-pruned': diff --git a/packages/capture-kit/src/snapshot/__tests__/snapshot-visibility-ios.test.ts b/packages/capture-kit/src/snapshot/__tests__/snapshot-visibility-ios.test.ts index 8a8f405ced..988d21dd7e 100644 --- a/packages/capture-kit/src/snapshot/__tests__/snapshot-visibility-ios.test.ts +++ b/packages/capture-kit/src/snapshot/__tests__/snapshot-visibility-ios.test.ts @@ -1,11 +1,11 @@ import { expect, test } from 'vitest'; import { attachRefs, type RawSnapshotNode } from '@agent-device/kernel/snapshot'; import { buildSnapshotVisibility } from '../snapshot-visibility.ts'; -import { presentIosInteractiveSnapshot } from '../../ios-snapshot-engine/index.ts'; +import { buildIosInteractiveSnapshotPresentation } from '../../ios-snapshot-engine/index.ts'; function buildSnapshotState(data: { nodes?: RawSnapshotNode[]; backend?: 'xctest' }) { return { - nodes: attachRefs(presentIosInteractiveSnapshot(data.nodes ?? [])), + nodes: attachRefs(buildIosInteractiveSnapshotPresentation(data.nodes ?? []).nodes), backend: data.backend, createdAt: Date.now(), }; diff --git a/packages/capture-kit/src/snapshot/snapshot-presentation/ios/action-shelf.test.ts b/packages/capture-kit/src/snapshot/snapshot-presentation/ios/action-shelf.test.ts index b335722621..6caa949e96 100644 --- a/packages/capture-kit/src/snapshot/snapshot-presentation/ios/action-shelf.test.ts +++ b/packages/capture-kit/src/snapshot/snapshot-presentation/ios/action-shelf.test.ts @@ -1,5 +1,5 @@ import { expect, test } from 'vitest'; -import { presentIosInteractiveSnapshot } from '../../../ios-snapshot-engine/index.ts'; +import { buildIosInteractiveSnapshotPresentation } from '../../../ios-snapshot-engine/index.ts'; import { closedComposerWithRetainedActionShelfNodes, closedComposerWithRetainedRegularTreeActionNodes, @@ -7,7 +7,9 @@ import { } from './transitions.fixtures.ts'; test('iOS presentation removes an action shelf whose child actions moved outside its viewport', () => { - const nodes = presentIosInteractiveSnapshot(closedComposerWithRetainedActionShelfNodes); + const nodes = buildIosInteractiveSnapshotPresentation( + closedComposerWithRetainedActionShelfNodes, + ).nodes; const labels = nodes.map((node) => node.label).filter(Boolean); expect(labels).toEqual([ @@ -21,14 +23,16 @@ test('iOS presentation removes an action shelf whose child actions moved outside }); test('iOS presentation removes retained regular-tree actions while the shelf toggle is collapsed', () => { - const nodes = presentIosInteractiveSnapshot(closedComposerWithRetainedRegularTreeActionNodes); + const nodes = buildIosInteractiveSnapshotPresentation( + closedComposerWithRetainedRegularTreeActionNodes, + ).nodes; expect(nodes.some((node) => node.label === 'action file')).toBe(false); expect(nodes.some((node) => node.identifier === 'GrowingTextView')).toBe(true); }); test('iOS presentation keeps an action shelf whose child actions are inside its viewport', () => { - const nodes = presentIosInteractiveSnapshot(openComposerActionShelfNodes); + const nodes = buildIosInteractiveSnapshotPresentation(openComposerActionShelfNodes).nodes; const actionLabels = nodes .map((node) => node.label) .filter((label) => label?.startsWith('action ')); diff --git a/packages/capture-kit/src/snapshot/snapshot-presentation/ios/rows.test.ts b/packages/capture-kit/src/snapshot/snapshot-presentation/ios/rows.test.ts index 1fe9ab67bf..8217e89b9f 100644 --- a/packages/capture-kit/src/snapshot/snapshot-presentation/ios/rows.test.ts +++ b/packages/capture-kit/src/snapshot/snapshot-presentation/ios/rows.test.ts @@ -1,9 +1,9 @@ import { expect, test } from 'vitest'; import { elementClassicRoomListNodes, legitimatelyLabeledCellNodes } from './rows.fixtures.ts'; -import { presentIosInteractiveSnapshot } from '../../../ios-snapshot-engine/index.ts'; +import { buildIosInteractiveSnapshotPresentation } from '../../../ios-snapshot-engine/index.ts'; test('iOS row presentation associates generic room cells with their descendant titles', () => { - const nodes = presentIosInteractiveSnapshot(elementClassicRoomListNodes); + const nodes = buildIosInteractiveSnapshotPresentation(elementClassicRoomListNodes).nodes; expect(nodes.filter((node) => node.type === 'Cell').map((node) => node.label)).toEqual([ 'Book Club', @@ -14,7 +14,7 @@ test('iOS row presentation associates generic room cells with their descendant t }); test('iOS row presentation preserves a legitimate no-space cell label', () => { - const nodes = presentIosInteractiveSnapshot(legitimatelyLabeledCellNodes); + const nodes = buildIosInteractiveSnapshotPresentation(legitimatelyLabeledCellNodes).nodes; expect(nodes.find((node) => node.type === 'Cell')?.label).toBe('StemCell'); }); diff --git a/packages/maestro/src/internal/__tests__/runtime-target-position.test.ts b/packages/maestro/src/internal/__tests__/runtime-target-position.test.ts index 380db6a87f..5900ca7f41 100644 --- a/packages/maestro/src/internal/__tests__/runtime-target-position.test.ts +++ b/packages/maestro/src/internal/__tests__/runtime-target-position.test.ts @@ -1,10 +1,7 @@ import { expect, test } from 'vitest'; -import { - rankMaestroCandidates, - selectMaestroPositionMatches, - selectMaestroSnapshotMatch, -} from '../runtime-target-ranking.ts'; +import { rankMaestroCandidates, selectMaestroSnapshotMatch } from '../runtime-target-ranking.ts'; import { makeSnapshot } from './runtime-target-fixtures.ts'; +import { selectMaestroPositionMatches } from '../runtime-target-ranking.fixtures.ts'; test('uses strict top-left comparisons for all positional relations', () => { const snapshot = makeSnapshot([ diff --git a/packages/maestro/src/internal/runtime-target-ranking.fixtures.ts b/packages/maestro/src/internal/runtime-target-ranking.fixtures.ts new file mode 100644 index 0000000000..ac8fa9cb5d --- /dev/null +++ b/packages/maestro/src/internal/runtime-target-ranking.fixtures.ts @@ -0,0 +1,16 @@ +import type { SnapshotNode, SnapshotState } from '@agent-device/kernel/snapshot'; +import type { MaestroSelector } from './program-ir.ts'; +import { resolveMaestroClickability } from './runtime-clickability.ts'; +import { createMaestroResolver } from './runtime-target-ranking.ts'; +import type { MaestroPositionRelation } from './runtime-target-position.ts'; +import type { MaestroPlatform } from './runtime-target-policy.ts'; + +export function selectMaestroPositionMatches( + snapshot: SnapshotState, + relation: MaestroPositionRelation, + anchor: MaestroSelector, + platform?: MaestroPlatform, +): SnapshotNode[] { + const clickability = platform ? resolveMaestroClickability(snapshot, platform) : undefined; + return createMaestroResolver(snapshot, clickability).resolvePosition(relation, anchor); +} diff --git a/packages/maestro/src/internal/runtime-target-ranking.ts b/packages/maestro/src/internal/runtime-target-ranking.ts index 291e3c8a50..fe13df1eec 100644 --- a/packages/maestro/src/internal/runtime-target-ranking.ts +++ b/packages/maestro/src/internal/runtime-target-ranking.ts @@ -10,7 +10,6 @@ import { type MaestroResolutionProbe, type MaestroSnapshotResolver, } from './runtime-selector-resolution.ts'; -import { type MaestroPositionRelation } from './runtime-target-position.ts'; import { filterVisibleMaestroMatches, type MaestroPlatform } from './runtime-target-policy.ts'; import { orderMaestroClickableFirst, @@ -66,16 +65,6 @@ export function matchMaestroCandidatesWithResolver( return { matches, parentMatched }; } -export function selectMaestroPositionMatches( - snapshot: SnapshotState, - relation: MaestroPositionRelation, - anchor: MaestroSelector, - platform?: MaestroPlatform, -): SnapshotNode[] { - const clickability = platform ? resolveMaestroClickability(snapshot, platform) : undefined; - return createMaestroResolver(snapshot, clickability).resolvePosition(relation, anchor); -} - export function createMaestroResolver( snapshot: SnapshotState, clickability: MaestroClickabilityDecision | undefined, diff --git a/packages/maestro/src/internal/conformance-normalize.ts b/packages/maestro/test/conformance/conformance-normalize.ts similarity index 99% rename from packages/maestro/src/internal/conformance-normalize.ts rename to packages/maestro/test/conformance/conformance-normalize.ts index 6719e1fc87..8309f73038 100644 --- a/packages/maestro/src/internal/conformance-normalize.ts +++ b/packages/maestro/test/conformance/conformance-normalize.ts @@ -11,8 +11,8 @@ import type { MaestroProgram, MaestroRunFlowCondition, MaestroSwipeGesture, -} from './program-ir.ts'; -import { MAESTRO_COMPATIBILITY_PRESETS } from './compatibility-policy.ts'; +} from '../../src/internal/program-ir.ts'; +import { MAESTRO_COMPATIBILITY_PRESETS } from '../../src/internal/compatibility-policy.ts'; import { asRecord, bool, dropUndefined, numLike, str } from './conformance-value-coercion.ts'; import { canonicalizeAgentSelector, diff --git a/packages/maestro/src/internal/__tests__/conformance-selector-projection.test.ts b/packages/maestro/test/conformance/conformance-selector-projection.test.ts similarity index 96% rename from packages/maestro/src/internal/__tests__/conformance-selector-projection.test.ts rename to packages/maestro/test/conformance/conformance-selector-projection.test.ts index 33e9932714..b27e385065 100644 --- a/packages/maestro/src/internal/__tests__/conformance-selector-projection.test.ts +++ b/packages/maestro/test/conformance/conformance-selector-projection.test.ts @@ -1,10 +1,10 @@ import { expect, test } from 'vitest'; -import { canonicalizeAgentCommands, canonicalizeUpstreamFlow } from '../conformance-normalize.ts'; +import { canonicalizeAgentCommands, canonicalizeUpstreamFlow } from './conformance-normalize.ts'; import { canonicalizeAgentSelector, canonicalizeUpstreamSelector, -} from '../conformance-selector-projection.ts'; -import { parseMaestroProgram } from '../program-ir-parser.ts'; +} from './conformance-selector-projection.ts'; +import { parseMaestroProgram } from '../../src/internal/program-ir-parser.ts'; test('canonicalizes command labels separately from selector identity', () => { const program = parseMaestroProgram( diff --git a/packages/maestro/src/internal/conformance-selector-projection.ts b/packages/maestro/test/conformance/conformance-selector-projection.ts similarity index 97% rename from packages/maestro/src/internal/conformance-selector-projection.ts rename to packages/maestro/test/conformance/conformance-selector-projection.ts index c16874e39d..5c8cd8e207 100644 --- a/packages/maestro/src/internal/conformance-selector-projection.ts +++ b/packages/maestro/test/conformance/conformance-selector-projection.ts @@ -1,4 +1,4 @@ -import type { MaestroGestureTarget, MaestroSelector } from './program-ir.ts'; +import type { MaestroGestureTarget, MaestroSelector } from '../../src/internal/program-ir.ts'; import { asRecord, bool, dropUndefined, numLike, str } from './conformance-value-coercion.ts'; export type CanonicalSelector = { diff --git a/packages/maestro/src/internal/conformance-value-coercion.ts b/packages/maestro/test/conformance/conformance-value-coercion.ts similarity index 100% rename from packages/maestro/src/internal/conformance-value-coercion.ts rename to packages/maestro/test/conformance/conformance-value-coercion.ts diff --git a/packages/maestro/test/conformance/harness.ts b/packages/maestro/test/conformance/harness.ts index 9377d2b5c9..9a5c7d42ad 100644 --- a/packages/maestro/test/conformance/harness.ts +++ b/packages/maestro/test/conformance/harness.ts @@ -2,7 +2,7 @@ import { canonicalizeAgentCommands, canonicalizeUpstreamFlow, type CanonicalCommand, -} from '../../src/internal/conformance-normalize.ts'; +} from './conformance-normalize.ts'; import { MAESTRO_COMPATIBILITY_PRESETS, MAESTRO_DEFAULT_SETTLE_TIMEOUT_MS, diff --git a/packages/maestro/test/conformance/layer2-tree.ts b/packages/maestro/test/conformance/layer2-tree.ts index ff8dd72998..57eee85453 100644 --- a/packages/maestro/test/conformance/layer2-tree.ts +++ b/packages/maestro/test/conformance/layer2-tree.ts @@ -2,10 +2,8 @@ import type { SnapshotNode, SnapshotState } from '@agent-device/kernel/snapshot' import { attachSnapshotClickabilityEvidence } from '@agent-device/contracts/capture'; import type { MaestroSelector } from '../../src/internal/program-ir.ts'; import { resolveMaestroTargetFromSnapshot } from '../../src/internal/runtime-targets.ts'; -import { - rankMaestroCandidates, - selectMaestroPositionMatches, -} from '../../src/internal/runtime-target-ranking.ts'; +import { rankMaestroCandidates } from '../../src/internal/runtime-target-ranking.ts'; +import { selectMaestroPositionMatches } from '../../src/internal/runtime-target-ranking.fixtures.ts'; export type Layer2TreeVector = { id: string; diff --git a/packages/platform-apple/src/runner/__tests__/runner-session-close-prep-fence.test.ts b/packages/platform-apple/src/runner/__tests__/runner-session-close-prep-fence.test.ts index 12e20b154e..0587ee314d 100644 --- a/packages/platform-apple/src/runner/__tests__/runner-session-close-prep-fence.test.ts +++ b/packages/platform-apple/src/runner/__tests__/runner-session-close-prep-fence.test.ts @@ -95,7 +95,8 @@ import { ensureRunnerSession, releaseIosRunnerOnClose, } from '../runner-session.ts'; -import { runnerPrepProcessChildren, runnerStartTeardownPending } from '../runner-xctestrun.ts'; +import { runnerPrepProcessChildren } from '../runner-xctestrun.ts'; +import { runnerStartTeardownPending } from '../runner-artifact.ts'; let projectRoot: string; let derived: string; diff --git a/packages/platform-apple/src/runner/__tests__/runner-start-admission.test.ts b/packages/platform-apple/src/runner/__tests__/runner-start-admission.test.ts index 0ee10d5de1..abf2423432 100644 --- a/packages/platform-apple/src/runner/__tests__/runner-start-admission.test.ts +++ b/packages/platform-apple/src/runner/__tests__/runner-start-admission.test.ts @@ -12,8 +12,8 @@ import { readmitRunnerStartAdmission, runnerStartAdmitsPreparation, runnerStartRetiredError, - runnerStartTeardownPending, } from '../runner-xctestrun.ts'; +import { runnerStartTeardownPending } from '../runner-artifact.ts'; // The start-admission decisions #3220 puts in one place: whether one start may still prepare, and // which cancellation is the last one. These are the primitives the spawn seam, the publish point, diff --git a/packages/platform-apple/src/runner/runner-xctestrun.ts b/packages/platform-apple/src/runner/runner-xctestrun.ts index 278554e7aa..3ed2714ced 100644 --- a/packages/platform-apple/src/runner/runner-xctestrun.ts +++ b/packages/platform-apple/src/runner/runner-xctestrun.ts @@ -18,7 +18,6 @@ export { runnerPrepProcessChildrenWithoutLiveOwner, runnerStartAdmitsPreparation, runnerStartRetiredError, - runnerStartTeardownPending, type RunnerStartAdmission, type RunnerXctestrunArtifact, type RunnerXctestrunArtifactState, diff --git a/packages/platform-apple/src/snapshot-route.test.ts b/packages/platform-apple/src/snapshot-route.test.ts index ddf50dfac2..092100a148 100644 --- a/packages/platform-apple/src/snapshot-route.test.ts +++ b/packages/platform-apple/src/snapshot-route.test.ts @@ -11,7 +11,7 @@ import { createRequestCanceledError } from '@agent-device/kernel/errors'; vi.mock('./system-surface-presence.ts', () => ({ createSystemSurfacePresenceProbe: () => async () => 'absent', })); -import { areIosSnapshotComparisonIdentitiesEqual } from '@agent-device/capture-kit/ios-snapshot-planning'; +import { iosSnapshotComparisonIdentityKey } from '@agent-device/capture-kit/ios-snapshot-planning'; import { IOS_SYSTEM_SURFACE_HOSTS } from '@agent-device/contracts/ios-system-surface'; import { simulatorAddressFor } from './core/simctl.ts'; import { mkdtempForTest } from './__tests__/tmp-dir.ts'; @@ -111,7 +111,8 @@ test('a presented system surface captures under the host lineage, never the app residue: [{ kind: 'fallback-source', producer: 'apple-runner' }], }); expect( - areIosSnapshotComparisonIdentitiesEqual(first.comparisonIdentity!, second.comparisonIdentity!), + iosSnapshotComparisonIdentityKey(first.comparisonIdentity!) === + iosSnapshotComparisonIdentityKey(second.comparisonIdentity!), ).toBe(true); // The bridge is healthy here and simply cannot see the surface, so the app's wording would lie. expect(first.warnings).toEqual([ @@ -140,12 +141,14 @@ test('a lingering probe cannot make a sheet capture and an app capture compare e }); expect(app.comparisonIdentity?.lineage).toEqual({ targetId: target.targetId }); expect( - areIosSnapshotComparisonIdentitiesEqual(sheet.comparisonIdentity!, app.comparisonIdentity!), + iosSnapshotComparisonIdentityKey(sheet.comparisonIdentity!) === + iosSnapshotComparisonIdentityKey(app.comparisonIdentity!), ).toBe(false); // Two app captures taken in the same lingering window still compare equal, so a poll can settle on // app content: the capture decides the lineage, and nothing here carries a per-capture residue. expect( - areIosSnapshotComparisonIdentitiesEqual(app.comparisonIdentity!, stillApp.comparisonIdentity!), + iosSnapshotComparisonIdentityKey(app.comparisonIdentity!) === + iosSnapshotComparisonIdentityKey(stillApp.comparisonIdentity!), ).toBe(true); expect(app.warnings).toEqual([ 'Simulator AX snapshot inapplicable (system-surface-host-lingering); used XCTest, which read app content: the system surface host process was still running but no longer presenting.', @@ -182,14 +185,13 @@ test('a sheet that appears after an absent probe is identified by the surface, n generation: target.generation, }); expect( - areIosSnapshotComparisonIdentitiesEqual(app.comparisonIdentity!, sheet.comparisonIdentity!), + iosSnapshotComparisonIdentityKey(app.comparisonIdentity!) === + iosSnapshotComparisonIdentityKey(sheet.comparisonIdentity!), ).toBe(false); // Two captures of the same sheet still compare equal, so a poll can settle on the sheet. expect( - areIosSnapshotComparisonIdentitiesEqual( - sheet.comparisonIdentity!, - sheetAgain.comparisonIdentity!, - ), + iosSnapshotComparisonIdentityKey(sheet.comparisonIdentity!) === + iosSnapshotComparisonIdentityKey(sheetAgain.comparisonIdentity!), ).toBe(true); // The reason the bridge was skipped survives — it is independent of what the runner found — but // the sentence cannot claim an app generation this capture did not read. @@ -216,10 +218,8 @@ test('a surface capture drops the app-generation residue of the arm that reached residue: [{ kind: 'fallback-source', producer: 'apple-runner' }], }); expect( - areIosSnapshotComparisonIdentitiesEqual( - sheet.comparisonIdentity!, - sheetAgain.comparisonIdentity!, - ), + iosSnapshotComparisonIdentityKey(sheet.comparisonIdentity!) === + iosSnapshotComparisonIdentityKey(sheetAgain.comparisonIdentity!), ).toBe(true); }); @@ -472,7 +472,8 @@ test('two target-resolution fallbacks cannot share comparison identity', async ( expect(first.comparisonIdentity).toBeDefined(); expect(second.comparisonIdentity).toBeDefined(); expect( - areIosSnapshotComparisonIdentitiesEqual(first.comparisonIdentity!, second.comparisonIdentity!), + iosSnapshotComparisonIdentityKey(first.comparisonIdentity!) === + iosSnapshotComparisonIdentityKey(second.comparisonIdentity!), ).toBe(false); }); diff --git a/packages/provider-webdriver/src/index.ts b/packages/provider-webdriver/src/index.ts index e031cc1b3b..e80ef7ee86 100644 --- a/packages/provider-webdriver/src/index.ts +++ b/packages/provider-webdriver/src/index.ts @@ -6,7 +6,6 @@ import type { } from './provider-plugin.ts'; import { createCloudWebDriverRuntime, type CloudWebDriverRuntime } from './runtime.ts'; -export { CLOUD_WEBDRIVER_PROVIDERS } from './providers.ts'; export type { RunHostCommand } from './dependencies.ts'; export type { BundledCloudWebDriverProvider, diff --git a/packages/replay-port/src/daemon-port/__tests__/target-classification.test.ts b/packages/replay-port/src/daemon-port/__tests__/target-classification.test.ts index a1e0e125c2..6dc7797658 100644 --- a/packages/replay-port/src/daemon-port/__tests__/target-classification.test.ts +++ b/packages/replay-port/src/daemon-port/__tests__/target-classification.test.ts @@ -6,7 +6,7 @@ import { computeTargetEvidence } from '@agent-device/selectors/target-evidence'; import { buildSelectorChainForNode, resolveRecordedTarget } from '@agent-device/selectors'; import { resolvePressRecordingTarget } from '@agent-device/selectors/press-retarget'; import { classifyReplayTarget } from '../target-classification.ts'; -import { resolveUnverifiedWrapperControl } from '@agent-device/selectors/interaction-targeting'; +import { resolveElementReportedTwice } from '@agent-device/selectors/interaction-targeting'; import { ELEMENT14_DISTINCT_SUBTREE_NODES, UNVERIFIED_HITTABILITY_WRAPPER_CHAIN_NODES, @@ -663,7 +663,7 @@ test('#1280 e2e: a retargeted press on a row container rebinds its labeled desce test('#2498 e2e: a read recorded on a collapsed wrapper chain verifies on the same tree', () => { const nodes = toSnapshotNodes(UNVERIFIED_HITTABILITY_WRAPPER_CHAIN_NODES); const reports = nodes.filter((node) => node.identifier === 'scoring_home_button'); - const control = resolveUnverifiedWrapperControl(nodes, reports); + const control = resolveElementReportedTwice(nodes, reports); assert.ok(control, 'dispatch resolves the toolbar button, not the item host that reports it'); const recorded = computeTargetEvidence({ node: control, preActionNodes: nodes }); assert.ok(recorded); diff --git a/packages/replay-port/src/target-classification-fixtures.ts b/packages/replay-port/src/target-classification.fixtures.ts similarity index 100% rename from packages/replay-port/src/target-classification-fixtures.ts rename to packages/replay-port/src/target-classification.fixtures.ts diff --git a/packages/selectors/src/index.ts b/packages/selectors/src/index.ts index 0b33e4f820..c49b28bce8 100644 --- a/packages/selectors/src/index.ts +++ b/packages/selectors/src/index.ts @@ -44,7 +44,6 @@ import { } from './internal/replay.ts'; export type { FindAction, FindLocator } from './internal/find.ts'; -export { IS_PREDICATES } from '@agent-device/contracts/is-predicate'; export type { IsPredicate } from './internal/predicates.ts'; export type { PolicyResolutionOutcome, diff --git a/packages/selectors/src/interaction-targeting.test.ts b/packages/selectors/src/interaction-targeting.test.ts index 89b6245643..0c9de2b9a1 100644 --- a/packages/selectors/src/interaction-targeting.test.ts +++ b/packages/selectors/src/interaction-targeting.test.ts @@ -12,7 +12,7 @@ import { classifyActionableTouchCandidates, createActionableTouchResolver, resolveActionableTouchResolution, - resolveUnverifiedWrapperControl, + resolveElementReportedTwice, } from './interaction-targeting.ts'; import { ELEMENT14_DISTINCT_SUBTREE_NODES, @@ -462,7 +462,7 @@ test('resolves the control of one ancestry chain through the rule on its own', ( ['XCUIElementTypeOther', 'XCUIElementTypeButton'], ); - const control = resolveUnverifiedWrapperControl(snapshot.nodes, reports); + const control = resolveElementReportedTwice(snapshot.nodes, reports); assert.equal(control?.type, 'XCUIElementTypeButton'); assert.equal(control?.index, 1); @@ -474,7 +474,7 @@ test('refuses a candidate set that is one report, not a pair to collapse', () => const snapshot = makeSnapshotState(UNVERIFIED_HITTABILITY_WRAPPER_CHAIN_NODES); const reports = identifierReports(snapshot, 'scoring_home_button'); - assert.equal(resolveUnverifiedWrapperControl(snapshot.nodes, [reports[1]!]), null); + assert.equal(resolveElementReportedTwice(snapshot.nodes, [reports[1]!]), null); }); test('refuses one ancestry chain of two real controls through the rule on its own', () => { @@ -485,7 +485,7 @@ test('refuses one ancestry chain of two real controls through the rule on its ow ['XCUIElementTypeCell', 'XCUIElementTypeButton'], ); - assert.equal(resolveUnverifiedWrapperControl(snapshot.nodes, reports), null); + assert.equal(resolveElementReportedTwice(snapshot.nodes, reports), null); }); test('refuses reports that sit in two separate ancestry chains', () => { @@ -524,6 +524,6 @@ test('refuses reports that sit in two separate ancestry chains', () => { ['XCUIElementTypeOther', 'XCUIElementTypeButton'], ); - assert.equal(resolveUnverifiedWrapperControl(snapshot.nodes, reports), null); + assert.equal(resolveElementReportedTwice(snapshot.nodes, reports), null); assert.equal(classifyActionableTouchCandidates(snapshot.nodes, reports).kind, 'ambiguous'); }); diff --git a/packages/selectors/src/interaction-targeting.ts b/packages/selectors/src/interaction-targeting.ts index 8edb8de6de..356f40c9b9 100644 --- a/packages/selectors/src/interaction-targeting.ts +++ b/packages/selectors/src/interaction-targeting.ts @@ -114,13 +114,6 @@ const WRAPPER_RECT_SLACK = 1; * to the descendant. Candidates carrying any hittability fact, and candidates * that do not form one ancestry chain, also keep the existing rules. */ -export function resolveUnverifiedWrapperControl( - nodes: SnapshotNode[], - candidates: readonly SnapshotNode[], -): SnapshotNode | null { - return resolveUnverifiedWrapperControlWithIndex(candidates, buildActionableTouchIndex(nodes)); -} - function resolveUnverifiedWrapperControlWithIndex( candidates: readonly SnapshotNode[], index: ActionableTouchIndex, diff --git a/scripts/ios-snapshot-differential.test.ts b/scripts/ios-snapshot-differential.test.ts index 0653b13dbd..7d490384c6 100644 --- a/scripts/ios-snapshot-differential.test.ts +++ b/scripts/ios-snapshot-differential.test.ts @@ -7,10 +7,12 @@ import { isGeometricallyActionable, isPositiveFiniteRect } from '@agent-device/k import type { Rect } from '@agent-device/kernel/snapshot'; import { compareDifferentialCases, - swiftToolchainAvailable, SWIFT_RUN_TIMEOUT_MS, - writeDifferentialFailureArtifact, } from '../packages/capture-kit/src/ios-snapshot-engine/conformance-harness.ts'; +import { + swiftToolchainAvailable, + writeDifferentialFailureArtifact, +} from '../packages/capture-kit/src/ios-snapshot-engine/conformance-harness.fixtures.ts'; import { differentialBatchArbitrary } from '../packages/capture-kit/src/ios-snapshot-engine/conformance-generator.ts'; import { readIosSnapshotEngineFixture } from '../packages/capture-kit/src/ios-snapshot-engine/conformance-fixture.ts'; import { diff --git a/scripts/layering/snapshot-assembly-presentation-policy.test.ts b/scripts/layering/snapshot-assembly-presentation-policy.test.ts index 9a1cf3e42a..8e0e808810 100644 --- a/scripts/layering/snapshot-assembly-presentation-policy.test.ts +++ b/scripts/layering/snapshot-assembly-presentation-policy.test.ts @@ -46,7 +46,7 @@ test('R74 rejects the assembly importing the iOS snapshot engine', () => { const result = violations( appended( assemblyFile, - `\nimport { presentIosInteractiveSnapshot } from '@agent-device/capture-kit/ios-snapshot-engine';\nvoid presentIosInteractiveSnapshot;\n`, + `\nimport { buildIosInteractiveSnapshotPresentation } from '@agent-device/capture-kit/ios-snapshot-engine';\nvoid buildIosInteractiveSnapshotPresentation;\n`, ), ); diff --git a/scripts/maestro-conformance/README.md b/scripts/maestro-conformance/README.md index 67106c731f..6ff716b573 100644 --- a/scripts/maestro-conformance/README.md +++ b/scripts/maestro-conformance/README.md @@ -97,10 +97,10 @@ failure, not a pass. teeth (`invalid/`). To add a flow: drop the `.yaml` in, add a note to `NOTES` in `build-manifest.mjs`, and regenerate. - [`fixtures/`](./fixtures) — the generated, checked-in layer-1/layer-2 captures. -- `packages/maestro/src/internal/conformance-normalize.ts` and - `conformance-selector-projection.ts` — package-private canonical projection and selector model. -- `packages/maestro/test/conformance/` — the deterministic verifier, declared divergences, - package-private harness, shared fixture seal, and layer-3 differential scenarios. Keeping this +- `packages/maestro/test/conformance/` — the canonical projection and selector model + (`conformance-normalize.ts`, `conformance-selector-projection.ts`), deterministic verifier, + declared divergences, package-private harness, shared fixture seal, and layer-3 differential + scenarios. Keeping this code under package tests prevents parser/canonicalization tooling from widening the production facade; regeneration imports the same package-owned seal implementation. - [`regenerate.mjs`](./regenerate.mjs) — SHA-verifies the jars, rebuilds the diff --git a/src/__tests__/is-argument-surface-parity.test.ts b/src/__tests__/is-argument-surface-parity.test.ts index 046c3ab265..ef7d2b188f 100644 --- a/src/__tests__/is-argument-surface-parity.test.ts +++ b/src/__tests__/is-argument-surface-parity.test.ts @@ -3,15 +3,11 @@ import fs from 'node:fs'; import path from 'node:path'; import { test } from 'vitest'; import { AppError } from '@agent-device/kernel/errors'; -import { - IS_PREDICATES, - checkIsArgs, - checkIsPredicate, - IS_PREDICATE_USAGE_HINT, -} from '@agent-device/selectors'; +import { checkIsArgs, checkIsPredicate, IS_PREDICATE_USAGE_HINT } from '@agent-device/selectors'; import { interactionCommandMetadata } from '../commands/interaction/metadata.ts'; import { readInputFromCli } from '../commands/cli-grammar.ts'; import type { CliFlags } from '@agent-device/contracts/command'; +import { IS_PREDICATES } from '@agent-device/contracts/is-predicate'; // Parity gate for the `is` argument contract across every surface that admits one. // diff --git a/src/__tests__/runtime-public.test.ts b/src/__tests__/runtime-public.test.ts index 8f296154d9..a7a0d34096 100644 --- a/src/__tests__/runtime-public.test.ts +++ b/src/__tests__/runtime-public.test.ts @@ -4,9 +4,7 @@ import path from 'node:path'; import { test } from 'vitest'; import { createAgentDevice, - createMemorySessionStore, localCommandPolicy, - restrictedCommandPolicy, type AgentDevice, type CommandSessionStore, } from '../runtime.ts'; @@ -19,6 +17,10 @@ import { type FileOutputRef, } from '../io.ts'; import { mkdtempForTestSync } from './test-utils/tmp-dir.ts'; +import { + createMemorySessionStore, + restrictedCommandPolicy, +} from '../command-runtime/runtime-factory.ts'; const backend = { platform: 'ios', diff --git a/src/commands/interaction/runtime/__tests__/settle-device-fixtures.ts b/src/commands/interaction/runtime/__tests__/settle-device-fixtures.ts index 80e5a67351..c90c7f6651 100644 --- a/src/commands/interaction/runtime/__tests__/settle-device-fixtures.ts +++ b/src/commands/interaction/runtime/__tests__/settle-device-fixtures.ts @@ -8,12 +8,9 @@ import type { AgentDeviceBackend, BackendSnapshotResult } from '../../../../backend.ts'; import type { SnapshotState } from '@agent-device/kernel/snapshot'; import { createLocalArtifactAdapter } from '../../../../io.ts'; -import { - createAgentDevice, - createMemorySessionStore, - localCommandPolicy, -} from '../../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../../runtime.ts'; import { makeSnapshotState } from '@agent-device/selectors/snapshot-geometry-fixtures'; +import { createMemorySessionStore } from '../../../../command-runtime/runtime-factory.ts'; export function createFakeClock(stepMs = 300): { now: () => number; diff --git a/src/commands/interaction/runtime/__tests__/test-utils/index.ts b/src/commands/interaction/runtime/__tests__/test-utils/index.ts index 8226c3e630..fb93699eb1 100644 --- a/src/commands/interaction/runtime/__tests__/test-utils/index.ts +++ b/src/commands/interaction/runtime/__tests__/test-utils/index.ts @@ -3,7 +3,6 @@ import { createLocalArtifactAdapter } from '../../../../../io.ts'; import type { SnapshotState } from '@agent-device/kernel/snapshot'; import { createAgentDevice, - createMemorySessionStore, localCommandPolicy, type CommandSessionStore, } from '../../../../../runtime.ts'; @@ -15,6 +14,7 @@ import { RN_TEXT_ECHO_OFFSET_RECT_NODES, UNVERIFIED_HITTABILITY_WRAPPER_CHAIN_NODES, } from '@agent-device/selectors/interaction-targeting-fixtures'; +import { createMemorySessionStore } from '../../../../../command-runtime/runtime-factory.ts'; export function selectorSnapshot(): SnapshotState { return makeSnapshotState([ @@ -84,31 +84,6 @@ export function offscreenDrawerSnapshot(): SnapshotState { ]); } -export function runtimeScrollSnapshot(options: { - hiddenBelow: boolean; - message?: string; -}): SnapshotState { - return makeSnapshotState([ - { - index: 1, - depth: 0, - type: 'ScrollView', - label: 'Messages', - hiddenContentBelow: options.hiddenBelow ? true : undefined, - rect: { x: 0, y: 100, width: 400, height: 600 }, - }, - { - index: 2, - depth: 1, - parentIndex: 1, - type: 'Button', - label: options.message ?? 'Latest message', - rect: { x: 0, y: 640, width: 400, height: 56 }, - hittable: true, - }, - ]); -} - export function fillableSnapshot(): SnapshotState { return makeSnapshotState([ { diff --git a/src/commands/interaction/runtime/interactions.test.ts b/src/commands/interaction/runtime/interactions.test.ts index e2430e3fc2..4d5e16851c 100644 --- a/src/commands/interaction/runtime/interactions.test.ts +++ b/src/commands/interaction/runtime/interactions.test.ts @@ -3,11 +3,7 @@ import { test } from 'vitest'; import type { AgentDeviceBackend } from '../../../backend.ts'; import { ref, selector } from './selector-read-utils.ts'; import { createLocalArtifactAdapter } from '../../../io.ts'; -import { - createAgentDevice, - createMemorySessionStore, - localCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; import type { Point, SnapshotState } from '@agent-device/kernel/snapshot'; import { summarizeAxEvidence } from '@agent-device/capture-kit/snapshot-evidence'; import { iosSystemSurfaceDisclosure } from '@agent-device/contracts/ios-system-surface'; @@ -20,6 +16,7 @@ import { offscreenDrawerSnapshot, selectorSnapshot, } from './__tests__/test-utils/index.ts'; +import { createMemorySessionStore } from '../../../command-runtime/runtime-factory.ts'; test('runtime click taps an explicit point without requiring a snapshot', async () => { const calls: Array<{ point: Point; count?: number }> = []; diff --git a/src/commands/interaction/runtime/selector-is.test.ts b/src/commands/interaction/runtime/selector-is.test.ts index acbb6339bd..a790158a61 100644 --- a/src/commands/interaction/runtime/selector-is.test.ts +++ b/src/commands/interaction/runtime/selector-is.test.ts @@ -2,15 +2,12 @@ import assert from 'node:assert/strict'; import { test } from 'vitest'; import type { AgentDeviceBackend, BackendSnapshotOptions } from '../../../backend.ts'; import { createLocalArtifactAdapter } from '../../../io.ts'; -import { - createAgentDevice, - createMemorySessionStore, - localCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; import { selector } from './selector-read-utils.ts'; import { makeSnapshotState } from '@agent-device/selectors/snapshot-geometry-fixtures'; import { createSelectorDevice, selectorReadSnapshot } from './__tests__/test-utils/index.ts'; import { AppError } from '@agent-device/kernel/errors'; +import { createMemorySessionStore } from '../../../command-runtime/runtime-factory.ts'; test('runtime selectors forward public snapshot options to backend capture', async () => { const snapshot = selectorReadSnapshot(); diff --git a/src/commands/interaction/runtime/selector-read.test.ts b/src/commands/interaction/runtime/selector-read.test.ts index d0adef041e..6017496dc5 100644 --- a/src/commands/interaction/runtime/selector-read.test.ts +++ b/src/commands/interaction/runtime/selector-read.test.ts @@ -4,7 +4,6 @@ import type { AgentDeviceBackend, BackendSnapshotOptions } from '../../../backen import { createLocalArtifactAdapter } from '../../../io.ts'; import { createAgentDevice, - createMemorySessionStore, localCommandPolicy, type CommandSessionStore, } from '../../../runtime.ts'; @@ -17,6 +16,7 @@ import { } from './__tests__/test-utils/index.ts'; import { AppError } from '@agent-device/kernel/errors'; import { STALE_REF_HINT } from '@agent-device/selectors'; +import { createMemorySessionStore } from '../../../command-runtime/runtime-factory.ts'; test('runtime get reads text from a selector target', async () => { const snapshot = selectorReadSnapshot(); diff --git a/src/commands/interaction/runtime/selector-wait-cancellation.test.ts b/src/commands/interaction/runtime/selector-wait-cancellation.test.ts index 464982362c..eeba5552a4 100644 --- a/src/commands/interaction/runtime/selector-wait-cancellation.test.ts +++ b/src/commands/interaction/runtime/selector-wait-cancellation.test.ts @@ -2,11 +2,8 @@ import assert from 'node:assert/strict'; import { test } from 'vitest'; import type { AgentDeviceBackend } from '../../../backend.ts'; import { createLocalArtifactAdapter } from '../../../io.ts'; -import { - createAgentDevice, - createMemorySessionStore, - localCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; +import { createMemorySessionStore } from '../../../command-runtime/runtime-factory.ts'; type Deferred = { promise: Promise; diff --git a/src/commands/interaction/runtime/settle-transition-baseline.test.ts b/src/commands/interaction/runtime/settle-transition-baseline.test.ts index b07d1e52e7..7426e54702 100644 --- a/src/commands/interaction/runtime/settle-transition-baseline.test.ts +++ b/src/commands/interaction/runtime/settle-transition-baseline.test.ts @@ -2,17 +2,14 @@ import assert from 'node:assert/strict'; import { test } from 'vitest'; import type { AgentDeviceBackend } from '../../../backend.ts'; import { createLocalArtifactAdapter } from '../../../io.ts'; -import { - createAgentDevice, - createMemorySessionStore, - localCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; import { elementSettledRoomSnapshot, elementThreadsNoticeSnapshot, elementTransientRoomSnapshot, } from './stable-capture.fixtures.ts'; import { settleAfterInteraction } from './settle.ts'; +import { createMemorySessionStore } from '../../../command-runtime/runtime-factory.ts'; test('settle recovers the session baseline when resolved target evidence is absent', async () => { let elapsedMs = 0; diff --git a/src/commands/interaction/runtime/settle.test.ts b/src/commands/interaction/runtime/settle.test.ts index 78c9959f35..53226a8e16 100644 --- a/src/commands/interaction/runtime/settle.test.ts +++ b/src/commands/interaction/runtime/settle.test.ts @@ -3,11 +3,7 @@ import { test } from 'vitest'; import type { AgentDeviceBackend } from '../../../backend.ts'; import type { SnapshotState } from '@agent-device/kernel/snapshot'; import { createLocalArtifactAdapter } from '../../../io.ts'; -import { - createAgentDevice, - createMemorySessionStore, - localCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; import { makeSnapshotState } from '@agent-device/selectors/snapshot-geometry-fixtures'; import { ref, selector } from './selector-read-utils.ts'; import { buildSettleTailEntries, NEVER_SETTLED_HINT } from './settle.ts'; @@ -18,6 +14,7 @@ import { welcomeSnapshot, } from './__tests__/settle-device-fixtures.ts'; import { readSnapshotQualityVerdict } from '@agent-device/capture-kit/snapshot-quality-verdict'; +import { createMemorySessionStore } from '../../../command-runtime/runtime-factory.ts'; // #1101 --settle: quiet-window settle loop composition on the interaction // commands. Budgets are injected (fake clock) — no real waiting. diff --git a/src/commands/interaction/runtime/stable-capture.test.ts b/src/commands/interaction/runtime/stable-capture.test.ts index 52e0034f52..2775c78494 100644 --- a/src/commands/interaction/runtime/stable-capture.test.ts +++ b/src/commands/interaction/runtime/stable-capture.test.ts @@ -3,11 +3,7 @@ import { test } from 'vitest'; import type { SnapshotState } from '@agent-device/kernel/snapshot'; import type { AgentDeviceBackend } from '../../../backend.ts'; import { createLocalArtifactAdapter } from '../../../io.ts'; -import { - createAgentDevice, - createMemorySessionStore, - localCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; import { runStableCaptureLoop } from './stable-capture.ts'; import { elementSettingsSnapshot, @@ -15,6 +11,7 @@ import { elementThreadsNoticeSnapshot, elementTransientRoomSnapshot, } from './stable-capture.fixtures.ts'; +import { createMemorySessionStore } from '../../../command-runtime/runtime-factory.ts'; const BROAD_TRANSITION_PARAMS = { quietMs: 500, diff --git a/src/commands/interaction/runtime/wait-absent.test.ts b/src/commands/interaction/runtime/wait-absent.test.ts index 88d69c0f27..d8b7beb04c 100644 --- a/src/commands/interaction/runtime/wait-absent.test.ts +++ b/src/commands/interaction/runtime/wait-absent.test.ts @@ -2,14 +2,11 @@ import assert from 'node:assert/strict'; import { test } from 'vitest'; import type { AgentDeviceBackend, BackendSnapshotResult } from '../../../backend.ts'; import { createLocalArtifactAdapter } from '../../../io.ts'; -import { - createAgentDevice, - createMemorySessionStore, - localCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; import { makeSnapshotState } from '@agent-device/selectors/snapshot-geometry-fixtures'; import { createFakeClock } from './__tests__/test-utils/index.ts'; import { AppError } from '@agent-device/kernel/errors'; +import { createMemorySessionStore } from '../../../command-runtime/runtime-factory.ts'; const SELECTOR = 'label="Removed"'; diff --git a/src/commands/interaction/runtime/wait-selector.test.ts b/src/commands/interaction/runtime/wait-selector.test.ts index e35ffba80a..4464bfd5e4 100644 --- a/src/commands/interaction/runtime/wait-selector.test.ts +++ b/src/commands/interaction/runtime/wait-selector.test.ts @@ -2,16 +2,13 @@ import assert from 'node:assert/strict'; import { test } from 'vitest'; import type { AgentDeviceBackend, BackendSnapshotOptions } from '../../../backend.ts'; import { createLocalArtifactAdapter } from '../../../io.ts'; -import { - createAgentDevice, - createMemorySessionStore, - localCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; import { makeSnapshotState } from '@agent-device/selectors/snapshot-geometry-fixtures'; import { createFakeClock } from './__tests__/test-utils/index.ts'; import { computeTargetEvidence } from '@agent-device/selectors/target-evidence'; import { WAIT_LANDMARK_MISMATCH_REASON } from '@agent-device/contracts/replay'; import { AppError } from '@agent-device/kernel/errors'; +import { createMemorySessionStore } from '../../../command-runtime/runtime-factory.ts'; test('runtime focused selector waits against a full snapshot', async () => { const snapshot = makeSnapshotState([ diff --git a/src/commands/interaction/runtime/wait-stable.test.ts b/src/commands/interaction/runtime/wait-stable.test.ts index 16f7298e98..7f0da4c051 100644 --- a/src/commands/interaction/runtime/wait-stable.test.ts +++ b/src/commands/interaction/runtime/wait-stable.test.ts @@ -2,13 +2,10 @@ import assert from 'node:assert/strict'; import { test } from 'vitest'; import type { AgentDeviceBackend, BackendSnapshotResult } from '../../../backend.ts'; import { createLocalArtifactAdapter } from '../../../io.ts'; -import { - createAgentDevice, - createMemorySessionStore, - localCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; import { makeSnapshotState } from '@agent-device/selectors/snapshot-geometry-fixtures'; import { createFakeClock, selectorReadSnapshot } from './__tests__/test-utils/index.ts'; +import { createMemorySessionStore } from '../../../command-runtime/runtime-factory.ts'; test('runtime wait stable settles after two unchanged captures', async () => { const snapshot = selectorReadSnapshot(); diff --git a/src/commands/management/runtime/admin-router.test.ts b/src/commands/management/runtime/admin-router.test.ts index 8f6fc2507e..75dbad6cc9 100644 --- a/src/commands/management/runtime/admin-router.test.ts +++ b/src/commands/management/runtime/admin-router.test.ts @@ -2,11 +2,8 @@ import assert from 'node:assert/strict'; import { test } from 'vitest'; import type { AgentDeviceBackend, BackendInstallSource } from '../../../backend.ts'; import type { ArtifactAdapter, FileInputRef } from '../../../io.ts'; -import { - createAgentDevice, - localCommandPolicy, - restrictedCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; +import { restrictedCommandPolicy } from '../../../command-runtime/runtime-factory.ts'; const artifacts = { resolveInput: async (ref: FileInputRef) => ({ diff --git a/src/commands/management/runtime/apps.test.ts b/src/commands/management/runtime/apps.test.ts index 13ce778010..80cb85c8ea 100644 --- a/src/commands/management/runtime/apps.test.ts +++ b/src/commands/management/runtime/apps.test.ts @@ -8,11 +8,8 @@ import type { } from '../../../backend.ts'; import type { JsonObject } from '@agent-device/contracts/client'; import { createLocalArtifactAdapter } from '../../../io.ts'; -import { - createAgentDevice, - localCommandPolicy, - restrictedCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; +import { restrictedCommandPolicy } from '../../../command-runtime/runtime-factory.ts'; test('runtime app commands call typed backend lifecycle primitives', async () => { const calls: unknown[] = []; diff --git a/src/commands/observability/runtime/diagnostics-router.test.ts b/src/commands/observability/runtime/diagnostics-router.test.ts index 37acdd08f9..62edf2336b 100644 --- a/src/commands/observability/runtime/diagnostics-router.test.ts +++ b/src/commands/observability/runtime/diagnostics-router.test.ts @@ -2,11 +2,11 @@ import assert from 'node:assert/strict'; import { test } from 'vitest'; import type { AgentDeviceBackend, BackendCommandContext } from '../../../backend.ts'; import type { ArtifactAdapter } from '../../../io.ts'; +import { createAgentDevice } from '../../../runtime.ts'; import { - createAgentDevice, createMemorySessionStore, restrictedCommandPolicy, -} from '../../../runtime.ts'; +} from '../../../command-runtime/runtime-factory.ts'; const artifacts = { resolveInput: async () => ({ path: '/tmp/input' }), diff --git a/src/commands/recording/runtime/contact-sheet.test.ts b/src/commands/recording/runtime/contact-sheet.test.ts index 53b22fda42..49260d7593 100644 --- a/src/commands/recording/runtime/contact-sheet.test.ts +++ b/src/commands/recording/runtime/contact-sheet.test.ts @@ -5,11 +5,8 @@ import { CONTACT_SHEET_THRESHOLD_REASON, } from '@agent-device/capture-kit/recording-contact-sheet'; import type { ArtifactAdapter, ArtifactDescriptor, FileInputRef } from '../../../io.ts'; -import { - createAgentDevice, - localCommandPolicy, - restrictedCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; +import { restrictedCommandPolicy } from '../../../command-runtime/runtime-factory.ts'; vi.mock('@agent-device/capture-kit/recording-contact-sheet', async (importOriginal) => ({ ...(await importOriginal()), diff --git a/src/commands/recording/runtime/recording.test.ts b/src/commands/recording/runtime/recording.test.ts index de24b67e7b..599d75bbd8 100644 --- a/src/commands/recording/runtime/recording.test.ts +++ b/src/commands/recording/runtime/recording.test.ts @@ -1,11 +1,8 @@ import assert from 'node:assert/strict'; import { test } from 'vitest'; import type { ArtifactAdapter, FileInputRef } from '../../../io.ts'; -import { - createAgentDevice, - localCommandPolicy, - restrictedCommandPolicy, -} from '../../../runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../runtime.ts'; +import { restrictedCommandPolicy } from '../../../command-runtime/runtime-factory.ts'; const artifacts = { resolveInput: async (ref: FileInputRef) => ({ diff --git a/src/core/__tests__/ios-snapshot-presentation-once.test.ts b/src/core/__tests__/ios-snapshot-presentation-once.test.ts index 08889c0e97..d0b43087fc 100644 --- a/src/core/__tests__/ios-snapshot-presentation-once.test.ts +++ b/src/core/__tests__/ios-snapshot-presentation-once.test.ts @@ -1,5 +1,5 @@ import { expect, test } from 'vitest'; -import { presentIosInteractiveSnapshot } from '@agent-device/capture-kit/ios-snapshot-engine'; +import { buildIosInteractiveSnapshotPresentation } from '@agent-device/capture-kit/ios-snapshot-engine'; import type { IosAcquisitionProducer, IosSnapshotProducer, @@ -91,7 +91,7 @@ const shape = (nodes: readonly RawSnapshotNode[]) => test('the collapsible fixture is not already a presentation fixed point', () => { const acquisitionNodes = collapsibleNodes(); - expect(presentIosInteractiveSnapshot(acquisitionNodes).length).toBeLessThan( + expect(buildIosInteractiveSnapshotPresentation(acquisitionNodes).nodes.length).toBeLessThan( acquisitionNodes.length, ); }); @@ -105,7 +105,9 @@ test.each(ACQUISITION_PRODUCERS)( const published = result.nodes ?? []; // Presented at least once: the engine's output is a fixed point of the presentation rules. - expect(shape(presentIosInteractiveSnapshot([...published]))).toEqual(shape(published)); + expect(shape(buildIosInteractiveSnapshotPresentation([...published]).nodes)).toEqual( + shape(published), + ); // Presented at most once: the daemon assembly passes the engine's tree through untouched. expect(shape(buildSnapshotState(result, { snapshotInteractiveOnly: true }).nodes)).toEqual( shape(published), diff --git a/src/daemon/__tests__/audio-probe-resource-recovery.test.ts b/src/daemon/__tests__/audio-probe-resource-recovery.test.ts index ba8f413497..b547a6a7de 100644 --- a/src/daemon/__tests__/audio-probe-resource-recovery.test.ts +++ b/src/daemon/__tests__/audio-probe-resource-recovery.test.ts @@ -12,13 +12,12 @@ import { deviceIdentity } from '@agent-device/kernel/device'; import { createDurableResourceEnvelope, createHostAudioProbeCaptureOperations, - encodeDurableDescriptor, - hostAudioProbeDescriptorCodec, } from '@agent-device/capture-kit'; import { unavailableDeploymentSnapshotAndShutdownOperationFacts } from '../../__tests__/test-utils/runtime-operation-facts.ts'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; import { recoverAudioProbeResourceAfterDaemonLock } from '@agent-device/capture-kit/audio-probe-recovery'; import { audioProbeResourceStore } from '@agent-device/capture-kit/audio-probe-resource-store'; +import { encodeHostAudioProbeDescriptor } from '@agent-device/capture-kit/audio-probe-descriptor-fixtures'; const device = { platform: 'apple' as const, @@ -53,7 +52,7 @@ test('recovery never finalizes a running checkpoint from a dead helper as comple owner: localRuntimeOwner('apple'), fence: { token: 'fence-1', generation: 1 }, lifecycle: 'open', - descriptor: encodeDurableDescriptor(hostAudioProbeDescriptorCodec, { + descriptor: encodeHostAudioProbeDescriptor({ backend: 'macos-screencapturekit', source: 'system-audio', sourceCount: 1, diff --git a/src/daemon/__tests__/snapshot-presentation-transitions.test.ts b/src/daemon/__tests__/snapshot-presentation-transitions.test.ts index 686a44f5b3..b652f81f56 100644 --- a/src/daemon/__tests__/snapshot-presentation-transitions.test.ts +++ b/src/daemon/__tests__/snapshot-presentation-transitions.test.ts @@ -2,13 +2,15 @@ import { expect, test } from 'vitest'; import { makeSnapshotState } from '@agent-device/selectors/snapshot-geometry-fixtures'; import { createInteractionDevice } from '../../commands/interaction/runtime/__tests__/test-utils/index.ts'; import { buildSnapshotState } from '@agent-device/capture-kit/snapshot-state'; -import { presentIosInteractiveSnapshot } from '@agent-device/capture-kit/ios-snapshot-engine'; +import { buildIosInteractiveSnapshotPresentation } from '@agent-device/capture-kit/ios-snapshot-engine'; import { navigationTitleWithAppProvidedDetailsAffordanceNodes } from '@agent-device/capture-kit/snapshot-presentation-ios-transitions-fixtures'; test('the daemon publishes engine transitions without reapplying runner-owned scope', () => { const snapshot = buildSnapshotState( { - nodes: presentIosInteractiveSnapshot(navigationTitleWithAppProvidedDetailsAffordanceNodes), + nodes: buildIosInteractiveSnapshotPresentation( + navigationTitleWithAppProvidedDetailsAffordanceNodes, + ).nodes, backend: 'xctest', producer: 'apple-runner', }, @@ -27,7 +29,9 @@ test('the daemon publishes engine transitions without reapplying runner-owned sc }); test('iOS presentation promotes an app-provided navigation title affordance without stealing a content action', () => { - const nodes = presentIosInteractiveSnapshot(navigationTitleWithAppProvidedDetailsAffordanceNodes); + const nodes = buildIosInteractiveSnapshotPresentation( + navigationTitleWithAppProvidedDetailsAffordanceNodes, + ).nodes; const titleActions = nodes.filter( (node) => node.type === 'Button' && node.label === 'Team Standup', ); @@ -47,7 +51,8 @@ test('iOS presentation promotes an app-provided navigation title affordance with test('promoted navigation title dispatches as its presented button semantics, not its disabled source field', async () => { const snapshot = makeSnapshotState( - presentIosInteractiveSnapshot(navigationTitleWithAppProvidedDetailsAffordanceNodes), + buildIosInteractiveSnapshotPresentation(navigationTitleWithAppProvidedDetailsAffordanceNodes) + .nodes, ); const title = snapshot.nodes.find((node) => node.identifier === 'DisplayNameTextField'); expect(title).toEqual(expect.objectContaining({ type: 'Button', enabled: true })); @@ -69,7 +74,7 @@ test('iOS presentation leaves a disabled navigation title field alone without a node.identifier === 'RoomDetailsIconImageView' ? { ...node, identifier: undefined } : node, ); - const nodes = presentIosInteractiveSnapshot(withoutNamedAffordance); + const nodes = buildIosInteractiveSnapshotPresentation(withoutNamedAffordance).nodes; expect( nodes.some((node) => node.type === 'TextField' && node.identifier === 'DisplayNameTextField'), ).toBe(true); diff --git a/src/daemon/__tests__/snapshot-publication-membership.test.ts b/src/daemon/__tests__/snapshot-publication-membership.test.ts index fafc415385..c76b6b0e8c 100644 --- a/src/daemon/__tests__/snapshot-publication-membership.test.ts +++ b/src/daemon/__tests__/snapshot-publication-membership.test.ts @@ -1,6 +1,6 @@ import { expect, test } from 'vitest'; import type { RawSnapshotNode } from '@agent-device/kernel/snapshot'; -import { presentIosInteractiveSnapshot } from '@agent-device/capture-kit/ios-snapshot-engine'; +import { buildIosInteractiveSnapshotPresentation } from '@agent-device/capture-kit/ios-snapshot-engine'; import { buildSnapshotState } from '@agent-device/capture-kit/snapshot-state'; // End-to-end publication-membership contract for the acquire/present design (#1797, external @@ -17,7 +17,11 @@ import { buildSnapshotState } from '@agent-device/capture-kit/snapshot-state'; // engine's presenter has to be the one that decides membership. function publish(nodes: RawSnapshotNode[]) { return buildSnapshotState( - { nodes: presentIosInteractiveSnapshot(nodes), backend: 'xctest', producer: 'apple-runner' }, + { + nodes: buildIosInteractiveSnapshotPresentation(nodes).nodes, + backend: 'xctest', + producer: 'apple-runner', + }, { snapshotInteractiveOnly: true }, ).nodes; } diff --git a/src/daemon/session-lifecycle/internal/__tests__/session-close-cwd-scoped-resources.test.ts b/src/daemon/session-lifecycle/internal/__tests__/session-close-cwd-scoped-resources.test.ts index 002a466fc7..012fbc97d4 100644 --- a/src/daemon/session-lifecycle/internal/__tests__/session-close-cwd-scoped-resources.test.ts +++ b/src/daemon/session-lifecycle/internal/__tests__/session-close-cwd-scoped-resources.test.ts @@ -3,11 +3,7 @@ import type { AppLogLiveHandle } from '@agent-device/contracts/app-log-runtime'; import type { AudioProbeLiveHandle } from '@agent-device/contracts/audio-probe-runtime'; import type { PerfNativeCaptureLiveHandle } from '@agent-device/contracts/perf-runtime'; import type { DurableResourceEnvelope } from '@agent-device/contracts/durable-resource-envelope'; -import { - createDurableResourceEnvelope, - encodeDurableDescriptor, - hostAudioProbeDescriptorCodec, -} from '@agent-device/capture-kit'; +import { createDurableResourceEnvelope } from '@agent-device/capture-kit'; import { appLogResourceStore } from '../../../app-log-resource-store.ts'; import { audioProbeResourceStore } from '@agent-device/capture-kit/audio-probe-resource-store'; import { perfCaptureResourceStore } from '@agent-device/capture-kit/perf-capture-resource-store'; @@ -18,6 +14,7 @@ import { type SessionState, } from './session-close-shutdown.fixtures.ts'; import { mkdtempForTestSync } from '../../../../__tests__/test-utils/tmp-dir.ts'; +import { encodeHostAudioProbeDescriptor } from '@agent-device/capture-kit/audio-probe-descriptor-fixtures'; const { handleSessionCommands, @@ -203,7 +200,7 @@ test('close finishes the audio probe of a cwd-scoped session by store address', owner: localRuntimeOwner('apple'), fence: { token: 'audio-probe-fence', generation: 1 }, lifecycle: 'open', - descriptor: encodeDurableDescriptor(hostAudioProbeDescriptorCodec, { + descriptor: encodeHostAudioProbeDescriptor({ backend: 'macos-screencapturekit', source: 'system-audio', sourceCount: 1, diff --git a/src/daemon/session-lifecycle/internal/__tests__/session-close-resource-cleanup.test.ts b/src/daemon/session-lifecycle/internal/__tests__/session-close-resource-cleanup.test.ts index d366a41a62..a1d49f05e8 100644 --- a/src/daemon/session-lifecycle/internal/__tests__/session-close-resource-cleanup.test.ts +++ b/src/daemon/session-lifecycle/internal/__tests__/session-close-resource-cleanup.test.ts @@ -1,16 +1,13 @@ import { beforeEach, expect, test, vi } from 'vitest'; import type { AudioProbeLiveHandle } from '@agent-device/contracts/audio-probe-runtime'; -import { - createDurableResourceEnvelope, - encodeDurableDescriptor, - hostAudioProbeDescriptorCodec, -} from '@agent-device/capture-kit'; +import { createDurableResourceEnvelope } from '@agent-device/capture-kit'; import { audioProbeResourceStore } from '@agent-device/capture-kit/audio-probe-resource-store'; import { sessionCloseShutdownFixture, type SessionState, } from './session-close-shutdown.fixtures.ts'; import { mkdtempForTestSync } from '../../../../__tests__/test-utils/tmp-dir.ts'; +import { encodeHostAudioProbeDescriptor } from '@agent-device/capture-kit/audio-probe-descriptor-fixtures'; const { AppError, @@ -110,7 +107,7 @@ test('close stops active host audio probe before deleting session', async () => owner: localRuntimeOwner('apple'), fence: { token: sessionName + '-fence', generation: 1 }, lifecycle: 'open', - descriptor: encodeDurableDescriptor(hostAudioProbeDescriptorCodec, { + descriptor: encodeHostAudioProbeDescriptor({ backend: 'macos-screencapturekit', source: 'system-audio', sourceCount: 1, diff --git a/src/daemon/session-observability/internal/__tests__/session-audio.test.ts b/src/daemon/session-observability/internal/__tests__/session-audio.test.ts index 7cf3bc0032..b20d5c4c38 100644 --- a/src/daemon/session-observability/internal/__tests__/session-audio.test.ts +++ b/src/daemon/session-observability/internal/__tests__/session-audio.test.ts @@ -8,11 +8,7 @@ import type { import type { AudioProbeResult } from '@agent-device/contracts/audio-probe-result'; import { localRuntimeOwner } from '@agent-device/contracts/platform-runtime'; import { deviceIdentity, type DeviceInfo } from '@agent-device/kernel/device'; -import { - createDurableResourceEnvelope, - encodeDurableDescriptor, - hostAudioProbeDescriptorCodec, -} from '@agent-device/capture-kit'; +import { createDurableResourceEnvelope } from '@agent-device/capture-kit'; import { IOS_DEVICE, WEB_DESKTOP_DEVICE, @@ -34,6 +30,7 @@ import type { SessionStore } from '../../../session-store.ts'; import type { DaemonResponse } from '../../../daemon-request.ts'; import { handleSessionObservabilityCommands } from '../../index.ts'; import { ANDROID_AUDIO_CONTRACT_EVIDENCE } from '../../__tests__/session-audio.coverage.ts'; +import { encodeHostAudioProbeDescriptor } from '@agent-device/capture-kit/audio-probe-descriptor-fixtures'; async function runAudio( params: Parameters[0], @@ -142,7 +139,7 @@ function fakeCaptureRuntime(device: DeviceInfo, status: AudioProbeResult) { owner, fence: input.fence, lifecycle: 'open', - descriptor: encodeDurableDescriptor(hostAudioProbeDescriptorCodec, { + descriptor: encodeHostAudioProbeDescriptor({ backend: 'macos-screencapturekit', source: 'system-audio', sourceCount: 1, diff --git a/src/runtime.ts b/src/runtime.ts index dc4bd11f9e..8e02198479 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -9,11 +9,7 @@ export type { CommandSessionRecord, CommandSessionStore, } from './runtime-contract.ts'; -export { - createMemorySessionStore, - localCommandPolicy, - restrictedCommandPolicy, -} from './command-runtime/runtime-factory.ts'; +export { localCommandPolicy } from './command-runtime/runtime-factory.ts'; export type AgentDevice = AgentDeviceRuntime & BoundAgentDeviceCommands; diff --git a/test/integration/interaction-contract/runtime-harness.ts b/test/integration/interaction-contract/runtime-harness.ts index 9a9fb8918d..2e980149d5 100644 --- a/test/integration/interaction-contract/runtime-harness.ts +++ b/test/integration/interaction-contract/runtime-harness.ts @@ -1,11 +1,8 @@ import type { AgentDeviceBackend } from '../../../src/backend.ts'; import type { SnapshotState } from '@agent-device/kernel/snapshot'; import { createLocalArtifactAdapter } from '../../../src/io.ts'; -import { - createAgentDevice, - createMemorySessionStore, - localCommandPolicy, -} from '../../../src/runtime.ts'; +import { createAgentDevice, localCommandPolicy } from '../../../src/runtime.ts'; +import { createMemorySessionStore } from '../../../src/command-runtime/runtime-factory.ts'; type ContractBackendOverrides = Partial< Pick< diff --git a/test/integration/provider-scenarios/cloud-webdriver-ios-text-entry.test.ts b/test/integration/provider-scenarios/cloud-webdriver-ios-text-entry.test.ts index 346ba02028..249bc050c7 100644 --- a/test/integration/provider-scenarios/cloud-webdriver-ios-text-entry.test.ts +++ b/test/integration/provider-scenarios/cloud-webdriver-ios-text-entry.test.ts @@ -1,9 +1,6 @@ import assert from 'node:assert/strict'; import { test } from 'vitest'; -import { - CLOUD_WEBDRIVER_PROVIDERS, - createBundledCloudWebDriverRuntimes, -} from '@agent-device/provider-webdriver'; +import { createBundledCloudWebDriverRuntimes } from '@agent-device/provider-webdriver'; import type { DeviceLease } from '@agent-device/contracts/device'; import { createProviderDeviceRuntimeRequestProviders } from '../../../src/provider-device-runtime.ts'; import type { DaemonRequest } from '../../../src/daemon/daemon-request.ts'; @@ -21,6 +18,7 @@ import { startCloudWebDriverTestServer, type StartedCloudWebDriverTestServer, } from './cloud-webdriver-test-server.ts'; +import { CLOUD_WEBDRIVER_PROVIDERS } from '@agent-device/provider-webdriver/providers'; const WEBDRIVER_PROVIDER = CLOUD_WEBDRIVER_PROVIDERS.browserStack; const CLIENT_VERSION = '0.20.6-test'; diff --git a/test/integration/provider-scenarios/cloud-webdriver-lease-http.test.ts b/test/integration/provider-scenarios/cloud-webdriver-lease-http.test.ts index fbaf45aabf..e9b9cdf7e6 100644 --- a/test/integration/provider-scenarios/cloud-webdriver-lease-http.test.ts +++ b/test/integration/provider-scenarios/cloud-webdriver-lease-http.test.ts @@ -1,10 +1,7 @@ import assert from 'node:assert/strict'; import http from 'node:http'; import { test } from 'vitest'; -import { - CLOUD_WEBDRIVER_PROVIDERS, - createBundledCloudWebDriverRuntimes, -} from '@agent-device/provider-webdriver'; +import { createBundledCloudWebDriverRuntimes } from '@agent-device/provider-webdriver'; import { createProviderDeviceRuntimeRequestProviders } from '../../../src/provider-device-runtime.ts'; import { createDaemonHttpServer } from '../../../src/daemon/server/http-server.ts'; import { buildHttpRpcPayload } from '../../../src/daemon-client/daemon-client-rpc.ts'; @@ -21,6 +18,7 @@ import { startCloudWebDriverTestServer, type StartedCloudWebDriverTestServer, } from './cloud-webdriver-test-server.ts'; +import { CLOUD_WEBDRIVER_PROVIDERS } from '@agent-device/provider-webdriver/providers'; const CLIENT_VERSION = '0.20.3-test'; diff --git a/test/integration/provider-scenarios/cloud-webdriver-provider-adapters.test.ts b/test/integration/provider-scenarios/cloud-webdriver-provider-adapters.test.ts index a2cb22e98f..dc63fd4165 100644 --- a/test/integration/provider-scenarios/cloud-webdriver-provider-adapters.test.ts +++ b/test/integration/provider-scenarios/cloud-webdriver-provider-adapters.test.ts @@ -5,7 +5,6 @@ import path from 'node:path'; import { test } from 'vitest'; import { AppError } from '@agent-device/kernel/errors'; import { - CLOUD_WEBDRIVER_PROVIDERS, bundledCloudWebDriverProvider, createBundledCloudWebDriverRuntimes, type RunHostCommand, @@ -29,6 +28,7 @@ import { import testMuPlugin from '@agent-device/testmu'; import { createPluginHost } from '../../../src/plugins/host.ts'; import { createCloudWebDriverRuntime } from '@agent-device/provider-webdriver/plugin'; +import { CLOUD_WEBDRIVER_PROVIDERS } from '@agent-device/provider-webdriver/providers'; const CLIENT_VERSION = '0.20.3-test'; diff --git a/test/integration/provider-scenarios/cloud-webdriver-provider-regressions.test.ts b/test/integration/provider-scenarios/cloud-webdriver-provider-regressions.test.ts index 43fba83dd1..b4ba80efe5 100644 --- a/test/integration/provider-scenarios/cloud-webdriver-provider-regressions.test.ts +++ b/test/integration/provider-scenarios/cloud-webdriver-provider-regressions.test.ts @@ -3,7 +3,6 @@ import fs from 'node:fs'; import path from 'node:path'; import { test, vi } from 'vitest'; import { - CLOUD_WEBDRIVER_PROVIDERS, createBundledCloudWebDriverRuntimes, type RunHostCommand, } from '@agent-device/provider-webdriver'; @@ -28,6 +27,7 @@ import { providerRegressionLease, providerRuntimeFor, } from './cloud-webdriver-regression-fixtures.ts'; +import { CLOUD_WEBDRIVER_PROVIDERS } from '@agent-device/provider-webdriver/providers'; test('AWS Device Farm endpoint selection skips live-control WebSocket URLs', async () => { await withProviderScenarioResource(ProviderRegressionServer.start, async (server) => { diff --git a/test/integration/provider-scenarios/cloud-webdriver-runtime.test.ts b/test/integration/provider-scenarios/cloud-webdriver-runtime.test.ts index b94724f6cd..f9fcbd5424 100644 --- a/test/integration/provider-scenarios/cloud-webdriver-runtime.test.ts +++ b/test/integration/provider-scenarios/cloud-webdriver-runtime.test.ts @@ -7,10 +7,7 @@ import { markRequestCanceled, registerRequestAbort, } from '@agent-device/host-kit/request'; -import { - CLOUD_WEBDRIVER_PROVIDERS, - createBundledCloudWebDriverRuntimes, -} from '@agent-device/provider-webdriver'; +import { createBundledCloudWebDriverRuntimes } from '@agent-device/provider-webdriver'; import type { CloudArtifact } from '@agent-device/contracts/observability'; import type { DeviceLease } from '@agent-device/contracts/device'; import { createProviderDeviceRuntimeRequestProviders } from '../../../src/provider-device-runtime.ts'; @@ -31,6 +28,7 @@ import { startCloudWebDriverTestServer, type StartedCloudWebDriverTestServer, } from './cloud-webdriver-test-server.ts'; +import { CLOUD_WEBDRIVER_PROVIDERS } from '@agent-device/provider-webdriver/providers'; const WEBDRIVER_PROVIDER = CLOUD_WEBDRIVER_PROVIDERS.browserStack; const CLIENT_VERSION = '0.20.3-test'; diff --git a/test/integration/provider-scenarios/stale-provider-runtime-admission.test.ts b/test/integration/provider-scenarios/stale-provider-runtime-admission.test.ts index f1772ea0ec..69c8cce22c 100644 --- a/test/integration/provider-scenarios/stale-provider-runtime-admission.test.ts +++ b/test/integration/provider-scenarios/stale-provider-runtime-admission.test.ts @@ -1,10 +1,7 @@ import { expect, test, vi } from 'vitest'; import type { DeviceLease, ProviderDeviceRuntime } from '@agent-device/contracts/device'; import type { PlatformRuntimeProviderModule } from '@agent-device/contracts/platform-runtime-operations'; -import { - CLOUD_WEBDRIVER_PROVIDERS, - createBundledCloudWebDriverRuntimes, -} from '@agent-device/provider-webdriver'; +import { createBundledCloudWebDriverRuntimes } from '@agent-device/provider-webdriver'; import { createLimrunRuntime, type LimrunRuntimeDependencies } from '@agent-device/provider-limrun'; import { limrunTestDependencies } from '../../../src/platform-runtime-gateway.fixtures.ts'; import type { DeviceInfo } from '@agent-device/kernel/device'; @@ -20,6 +17,7 @@ import { awsRegressionContext, } from './cloud-webdriver-regression-fixtures.ts'; import { inspectProviderDeploymentAdmission } from './stale-provider-runtime-admission.fixtures.ts'; +import { CLOUD_WEBDRIVER_PROVIDERS } from '@agent-device/provider-webdriver/providers'; const limrunState = vi.hoisted(() => ({ androidCreate: vi.fn(async () => ({ diff --git a/vitest.config.ts b/vitest.config.ts index 48d4b59179..7abf9d88c2 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -103,6 +103,9 @@ export default defineConfig({ include: [ 'src/**/*.test.ts', 'packages/*/src/**/*.test.ts', + // The maestro conformance projection is test-tree code beside its harness; its own + // test stays in the fast lane rather than the node:test conformance script. + 'packages/maestro/test/conformance/conformance-selector-projection.test.ts', // The subprocess watchdog self-check: spawns a real node subprocess per case, // and one hangs on purpose (#1414). 'scripts/fuzz/harness.test.ts', From 9a76113e0c345a4bed4b053d228e89aa6ab7f6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Sat, 10 Oct 2026 16:53:20 +0200 Subject: [PATCH 2/4] fix(capture-kit): shell-quote the saved replay path and keep DifferentialCase private The failure artifact's replay command quoted the case path with JSON.stringify, which leaves $ and backticks live inside the double quotes; shellQuoteIfNeeded is the shared host-side quoting primitive. The fixtures derive DifferentialCase from compareDifferentialCases, as the generator already does, instead of widening the harness exports. Co-Authored-By: Claude Opus 5.5 --- .../ios-snapshot-engine/conformance-harness.fixtures.ts | 7 +++++-- .../src/ios-snapshot-engine/conformance-harness.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.fixtures.ts b/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.fixtures.ts index a68680a94a..b2dbc0fff2 100644 --- a/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.fixtures.ts +++ b/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.fixtures.ts @@ -2,7 +2,10 @@ import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import { mkdtempForTestSync } from '../tmp-dir.fixtures.ts'; -import type { DifferentialCase } from './conformance-harness.ts'; +import { shellQuoteIfNeeded } from '@agent-device/kernel/device-shell'; +import type { compareDifferentialCases } from './conformance-harness.ts'; + +type DifferentialCase = Parameters[0][number]; export function swiftToolchainAvailable(): boolean { if (process.platform !== 'darwin') return false; @@ -27,7 +30,7 @@ export function writeDifferentialFailureArtifact(input: { const replayCommand = [ 'node --experimental-strip-types', 'packages/capture-kit/src/ios-snapshot-engine/replay.ts', - JSON.stringify(casePath), + shellQuoteIfNeeded(casePath), ].join(' '); fs.writeFileSync( path.join(directory, 'replay-command.txt'), diff --git a/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.ts b/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.ts index 77afbf4647..b254015252 100644 --- a/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.ts +++ b/packages/capture-kit/src/ios-snapshot-engine/conformance-harness.ts @@ -16,7 +16,7 @@ export const SWIFT_RUN_TIMEOUT_MS = 60_000; let swiftHarnessExecutable: string | undefined; -export type DifferentialCase = Readonly<{ +type DifferentialCase = Readonly<{ name: string; route: 'acquired' | 'runner-presented'; projection: 'regular' | 'raw'; From 8d668a8a028570fb3ef1194276cf7b416d487146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Sat, 10 Oct 2026 18:25:52 +0200 Subject: [PATCH 3/4] refactor: rename the maestro position fixture and retitle the wrapper-control tests Review follow-up. resolveMaestroPositionForTest no longer shares a name with the production selectMaestroPositionMatches, and the wrapper-control tests are titled for the composite resolveElementReportedTwice they now call. Co-Authored-By: Claude Opus 5.5 --- .../src/internal/__tests__/runtime-target-position.test.ts | 4 ++-- .../maestro/src/internal/runtime-target-ranking.fixtures.ts | 2 +- packages/maestro/test/conformance/layer2-tree.ts | 4 ++-- packages/selectors/src/interaction-targeting.test.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/maestro/src/internal/__tests__/runtime-target-position.test.ts b/packages/maestro/src/internal/__tests__/runtime-target-position.test.ts index 5900ca7f41..fb8fa02c42 100644 --- a/packages/maestro/src/internal/__tests__/runtime-target-position.test.ts +++ b/packages/maestro/src/internal/__tests__/runtime-target-position.test.ts @@ -1,7 +1,7 @@ import { expect, test } from 'vitest'; import { rankMaestroCandidates, selectMaestroSnapshotMatch } from '../runtime-target-ranking.ts'; import { makeSnapshot } from './runtime-target-fixtures.ts'; -import { selectMaestroPositionMatches } from '../runtime-target-ranking.fixtures.ts'; +import { resolveMaestroPositionForTest } from '../runtime-target-ranking.fixtures.ts'; test('uses strict top-left comparisons for all positional relations', () => { const snapshot = makeSnapshot([ @@ -35,7 +35,7 @@ test('keeps relative pair distance as intermediate order but preserves composed { index: 2, identifier: 'near-anchor', rect: { x: 40, y: 40, width: 40, height: 30 } }, { index: 3, identifier: 'far-anchor', rect: { x: 300, y: 40, width: 40, height: 30 } }, ]); - const relation = selectMaestroPositionMatches(snapshot, 'below', { id: '.*anchor' }); + const relation = resolveMaestroPositionForTest(snapshot, 'below', { id: '.*anchor' }); expect(relation.map((node) => node.identifier)).toEqual([ 'near-candidate', 'far-candidate', diff --git a/packages/maestro/src/internal/runtime-target-ranking.fixtures.ts b/packages/maestro/src/internal/runtime-target-ranking.fixtures.ts index ac8fa9cb5d..132536e8fe 100644 --- a/packages/maestro/src/internal/runtime-target-ranking.fixtures.ts +++ b/packages/maestro/src/internal/runtime-target-ranking.fixtures.ts @@ -5,7 +5,7 @@ import { createMaestroResolver } from './runtime-target-ranking.ts'; import type { MaestroPositionRelation } from './runtime-target-position.ts'; import type { MaestroPlatform } from './runtime-target-policy.ts'; -export function selectMaestroPositionMatches( +export function resolveMaestroPositionForTest( snapshot: SnapshotState, relation: MaestroPositionRelation, anchor: MaestroSelector, diff --git a/packages/maestro/test/conformance/layer2-tree.ts b/packages/maestro/test/conformance/layer2-tree.ts index 57eee85453..a9cf54fa77 100644 --- a/packages/maestro/test/conformance/layer2-tree.ts +++ b/packages/maestro/test/conformance/layer2-tree.ts @@ -3,7 +3,7 @@ import { attachSnapshotClickabilityEvidence } from '@agent-device/contracts/capt import type { MaestroSelector } from '../../src/internal/program-ir.ts'; import { resolveMaestroTargetFromSnapshot } from '../../src/internal/runtime-targets.ts'; import { rankMaestroCandidates } from '../../src/internal/runtime-target-ranking.ts'; -import { selectMaestroPositionMatches } from '../../src/internal/runtime-target-ranking.fixtures.ts'; +import { resolveMaestroPositionForTest } from '../../src/internal/runtime-target-ranking.fixtures.ts'; export type Layer2TreeVector = { id: string; @@ -44,7 +44,7 @@ export function checkLayer2TreeVector(vector: Layer2TreeVector): Layer2TreeResul const actualSelected = resolution.ok ? [vector.nodes[resolution.node.index]?.key ?? ''] : []; const expectedSelected = vector.selected === undefined ? [] : [vector.selected]; const actualIntermediate = vector.intermediateRelation - ? selectMaestroPositionMatches( + ? resolveMaestroPositionForTest( snapshot, vector.intermediateRelation, vector.selector[vector.intermediateRelation]!, diff --git a/packages/selectors/src/interaction-targeting.test.ts b/packages/selectors/src/interaction-targeting.test.ts index 0c9de2b9a1..284fd2ce56 100644 --- a/packages/selectors/src/interaction-targeting.test.ts +++ b/packages/selectors/src/interaction-targeting.test.ts @@ -454,7 +454,7 @@ function identifierReports( return snapshot.nodes.filter((node) => node.identifier === identifier); } -test('resolves the control of one ancestry chain through the rule on its own', () => { +test('resolves the control of one ancestry chain through resolveElementReportedTwice', () => { const snapshot = makeSnapshotState(UNVERIFIED_HITTABILITY_WRAPPER_CHAIN_NODES); const reports = identifierReports(snapshot, 'scoring_home_button'); assert.deepEqual( @@ -477,7 +477,7 @@ test('refuses a candidate set that is one report, not a pair to collapse', () => assert.equal(resolveElementReportedTwice(snapshot.nodes, [reports[1]!]), null); }); -test('refuses one ancestry chain of two real controls through the rule on its own', () => { +test('refuses one ancestry chain of two real controls through resolveElementReportedTwice', () => { const snapshot = makeSnapshotState(TWO_ACTIONABLE_WRAPPER_CHAIN_NODES); const reports = identifierReports(snapshot, 'profile'); assert.deepEqual( From 600c3cb396b04d8ef3b9385e1d33f13745057c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Sat, 10 Oct 2026 18:25:52 +0200 Subject: [PATCH 4/4] chore(gates): record the production-export seams fallow cannot see Exports maps and the capture-kit subpath pin follow the fixture moves. The moved conformance projection gets new health-baseline entries for its pre-existing complexity findings: fallow 2.104's audit treats a moved file as changed, while the old path needed no entry because no change touched it. The .fallowrc entries name exports whose consumers sit outside fallow's --production graph: a published provider's plugin default export, a member read off a lazy import(), contracts facade members the facade-exhaustiveness rule requires, two test-tree seams with no fixture subpath (.ad grammar drift guard, recovery authority), the recording-path contract, and the staged managed-local lease foundations, which go with their feature rather than export by export. Co-Authored-By: Claude Opus 5.5 --- .fallowrc.json | 51 ++++++++++++++++++++- fallow-baselines/health.json | 8 ++++ packages/capture-kit/package.json | 4 ++ packages/replay-port/package.json | 4 +- scripts/layering/package-boundaries.test.ts | 1 + 5 files changed, 64 insertions(+), 4 deletions(-) diff --git a/.fallowrc.json b/.fallowrc.json index 324731af61..d0b1cfd374 100644 --- a/.fallowrc.json +++ b/.fallowrc.json @@ -82,6 +82,11 @@ "file": "packages/provider-*/test/package-smoke.mjs", "exports": ["*"] }, + { + "comment": "A published provider package's default export is its plugin contract; src/plugins/load.ts imports the package by name at runtime and reads `default`.", + "file": "packages/provider-*/src/index.ts", + "exports": ["default"] + }, { "comment": "Android perf mechanics are selected through the lazy platform host so importing agent-device does not eagerly load adb mechanics. Fallow cannot follow the dynamic property read in src/platform-runtime-perf-host.ts.", "file": "packages/platform-android/src/perf.ts", @@ -92,6 +97,11 @@ "file": "packages/platform-apple/src/core/perf.ts", "exports": ["buildAppleFrameSamplingMetadata", "buildAppleMemorySamplingMetadata"] }, + { + "comment": "packages/platform-apple/src/lifecycle.ts loads this module on demand and reads the member off the memoized import() promise, which Fallow cannot follow.", + "file": "packages/platform-apple/src/expo-dev-client-launch.ts", + "exports": ["resolveExpoDevClientLaunchUrl"] + }, { "comment": "These host-process helpers are consumed only by integration leak-oracle support and host-process unit tests; Fallow's production graph intentionally excludes both consumers.", "file": "packages/host-kit/src/process.ts", @@ -286,6 +296,15 @@ "file": "packages/contracts/src/facades/snapshot.ts", "exports": ["findSnapshotScopeRange"] }, + { + "comment": "Same façade-exhaustiveness rule (scripts/layering/facade-surface.test.ts): these façades must re-export every symbol of ../screenshot.ts, ../remote-config-fields.ts and ../snapshot-visibility.ts, whose production callers import the source module directly.", + "file": "packages/contracts/src/facades/{capture,remote,snapshot}.ts", + "exports": [ + "SCREENSHOT_ACTION_FLAG_KEYS", + "PROVIDER_PROFILE_FIELD_FLAG_ALIASES", + "collectViewportRects" + ] + }, { "comment": "Test fixtures: every *.fixtures.ts builds doubles for co-located *.test.ts files (several import `vi` directly) and none is imported by production source, so --production analysis sees no consumer for any of them. Scoped to unused-exports rather than ignorePatterns so fixtures stay inside every other Fallow rule (health, dead-code, cycles); `*` rather than today's three symbols because the property being asserted is per-file, not per-symbol.", "file": "**/*.fixtures.ts", @@ -349,14 +368,42 @@ "exports": ["getSessionCommandKind"] }, { - "comment": "Workspace-package symbols whose only consumers are test-tree files, which --production analysis does not see. Each import is the package specifier, so the façade entry is load-bearing: parseTargetAnnotationV1Payload/TARGET_ANNOTATION_MAX_FIELD_BYTES from src/daemon/__tests__/session-target-evidence.test.ts, MAESTRO_COMPAT_* from src/cli/parser/__tests__/maestro-support-matrix.test.ts, createAppLogLiveHandle from src/__tests__/test-utils/app-log-live-handle.ts (its production sibling createAppLogLiveHandleFromFinish wraps it, and scripts/layering/platform-package-policy.test.ts pins the specifier form).", + "comment": "Workspace-package symbols whose only consumers are test-tree files, which --production analysis does not see. Each import is the package specifier, so the façade entry is load-bearing: parseTargetAnnotationV1Payload/TARGET_ANNOTATION_MAX_FIELD_BYTES from src/daemon/__tests__/session-target-evidence.test.ts, MAESTRO_COMPAT_* from src/cli/parser/__tests__/maestro-support-matrix.test.ts, SCRIPT_FLAG_COMMANDS/scriptFlagEntries from src/commands/replay/script-flag-admission.test.ts (the #3197 two-way drift guard between the .ad grammar and the CLI schema), createAppLogLiveHandle from src/__tests__/test-utils/app-log-live-handle.ts (its production sibling createAppLogLiveHandleFromFinish wraps it, and scripts/layering/platform-package-policy.test.ts pins the specifier form).", "file": "packages/{ad-script,maestro,capture-kit}/src/index.ts", "exports": [ "parseTargetAnnotationV1Payload", "TARGET_ANNOTATION_MAX_FIELD_BYTES", "MAESTRO_COMPAT_LIMITATIONS", "MAESTRO_COMPAT_SUPPORTED_CAPABILITIES", - "createAppLogLiveHandle" + "createAppLogLiveHandle", + "SCRIPT_FLAG_COMMANDS", + "scriptFlagEntries" + ] + }, + { + "comment": "src/daemon/__tests__/request-runtime-binding.test.ts drives the daemon's exact bind through the recovery authority, whose cancellation race is what rejects; production reaches it only through finishRecoveredDurableCapture and recoverDurableCaptureResource in the same package.", + "file": "packages/capture-kit/src/durable-capture/index.ts", + "exports": ["acquireDurableCaptureRecoveryAuthorityBeforeDeadline"] + }, + { + "comment": "The collected copy's path is part of the stop-sequence contract (ADR 0024 §2.3) beside nativeRecordingPath; provider-scenario and Apple recovery tests assert pulls land on it, while production derives it inside the stop sequence.", + "file": "packages/capture-kit/src/recording/stop-sequence.ts", + "exports": ["collectedRecordingPath"] + }, + { + "comment": "Staged managed-local lease foundations (ADR 0021, #2284/#2308): src/daemon/request-runtime-binding.ts enters request admission for managed-local owners, but the allocator activation unit that would construct these journals, stores, claims and admissions never landed. Remove with the feature, not export by export: its owner kind is part of persisted device-claim and durable-resource records.", + "file": "{packages/managed-allocation/src/{journal,record,store}.ts,src/daemon/device/device-claim-allocator.ts,src/daemon/managed-device-allocation/lease-admission.ts,src/managed-device-reachability.ts}", + "exports": [ + "createAllocationOperationJournal", + "ALLOCATION_OPERATION_SCHEMA_VERSION", + "newAllocationOperation", + "decodeAllocationOperationRecord", + "bindingFenceFor", + "createAllocationOperationStore", + "acquireAllocatorHeldDeviceClaim", + "releaseAllocatorHeldClaim", + "createManagedLeaseAdmission", + "createManagedLeaseReachability" ] }, { diff --git a/fallow-baselines/health.json b/fallow-baselines/health.json index 5da0a54928..4044760049 100644 --- a/fallow-baselines/health.json +++ b/fallow-baselines/health.json @@ -529,6 +529,14 @@ "crap_high": { "count": 1 } + }, + "packages/maestro/test/conformance/conformance-normalize.ts": { + "complexity_critical": { + "count": 2 + }, + "crap_critical": { + "count": 2 + } } }, "runtime_coverage_findings": [], diff --git a/packages/capture-kit/package.json b/packages/capture-kit/package.json index 7a8943f541..05e8112ceb 100644 --- a/packages/capture-kit/package.json +++ b/packages/capture-kit/package.json @@ -22,6 +22,10 @@ "types": "./src/capture-admission/audio-probe-admission-ledger.ts", "default": "./src/capture-admission/audio-probe-admission-ledger.ts" }, + "./audio-probe-descriptor-fixtures": { + "types": "./src/audio-probe-descriptor.fixtures.ts", + "default": "./src/audio-probe-descriptor.fixtures.ts" + }, "./audio-probe-recovery": { "types": "./src/capture-admission/audio-probe-resource-recovery.ts", "default": "./src/capture-admission/audio-probe-resource-recovery.ts" diff --git a/packages/replay-port/package.json b/packages/replay-port/package.json index 2aecce22a2..0ab0a0a547 100644 --- a/packages/replay-port/package.json +++ b/packages/replay-port/package.json @@ -137,8 +137,8 @@ "default": "./src/daemon-port/target-classification.ts" }, "./target-classification-fixtures": { - "types": "./src/target-classification-fixtures.ts", - "default": "./src/target-classification-fixtures.ts" + "types": "./src/target-classification.fixtures.ts", + "default": "./src/target-classification.fixtures.ts" }, "./target-token": { "types": "./src/daemon-port/target-token.ts", diff --git a/scripts/layering/package-boundaries.test.ts b/scripts/layering/package-boundaries.test.ts index 2586039eb1..b210d218b8 100644 --- a/scripts/layering/package-boundaries.test.ts +++ b/scripts/layering/package-boundaries.test.ts @@ -439,6 +439,7 @@ test('the real tree parses, declares, and passes R11', () => { '@agent-device/capture-kit', '@agent-device/capture-kit/android-replacement-surface-occlusion', '@agent-device/capture-kit/audio-probe-admission-ledger', + '@agent-device/capture-kit/audio-probe-descriptor-fixtures', '@agent-device/capture-kit/audio-probe-recovery', '@agent-device/capture-kit/audio-probe-resource-store', '@agent-device/capture-kit/audio-probe-session-resource',