From 3e315d846e95b44e13cceb5dfa30850e51b5d101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Mon, 28 Sep 2026 13:26:47 +0200 Subject: [PATCH 1/3] refactor(replay): move test outcomes and support values into replay-port --- packages/replay-port/package.json | 29 +++++++++++++++++++ .../session-replay-runtime-artifacts.test.ts | 3 +- .../session-test-infrastructure.test.ts | 5 ++-- .../__tests__/session-test-outcome.test.ts | 2 +- .../session-replay-report-action.ts | 0 .../session-replay-runtime-artifacts.ts | 0 .../session-replay-suggestion-ranking.ts | 0 .../session-replay-test-policy.ts | 0 .../src/daemon-port}/session-replay-trace.ts | 0 .../session-test-infrastructure.ts | 0 .../src/daemon-port}/session-test-outcome.ts | 0 pnpm-lock.yaml | 3 ++ ...ion-test-suite-command-flag-policy.test.ts | 2 +- .../internal/session-replay-action-runtime.ts | 2 +- .../internal/session-replay-divergence.ts | 4 +-- .../replay/internal/session-replay-heal.ts | 2 +- .../session-replay-maestro-failure.ts | 4 +-- .../session-replay-maestro-observer.ts | 2 +- .../session-replay-runtime-engine-adapter.ts | 2 +- .../session-replay-video-recording.ts | 2 +- src/daemon/replay/internal/test-command.ts | 9 ++++-- 21 files changed, 53 insertions(+), 18 deletions(-) rename {src/daemon/replay/internal => packages/replay-port/src/daemon-port}/__tests__/session-replay-runtime-artifacts.test.ts (93%) rename {src/daemon/replay/internal => packages/replay-port/src/daemon-port}/__tests__/session-test-infrastructure.test.ts (94%) rename {src/daemon/replay/internal => packages/replay-port/src/daemon-port}/__tests__/session-test-outcome.test.ts (96%) rename {src/daemon/replay/internal => packages/replay-port/src/daemon-port}/session-replay-report-action.ts (100%) rename {src/daemon/replay/internal => packages/replay-port/src/daemon-port}/session-replay-runtime-artifacts.ts (100%) rename {src/daemon/replay/internal => packages/replay-port/src/daemon-port}/session-replay-suggestion-ranking.ts (100%) rename {src/daemon/replay/internal => packages/replay-port/src/daemon-port}/session-replay-test-policy.ts (100%) rename {src/daemon/replay/internal => packages/replay-port/src/daemon-port}/session-replay-trace.ts (100%) rename {src/daemon/replay/internal => packages/replay-port/src/daemon-port}/session-test-infrastructure.ts (100%) rename {src/daemon/replay/internal => packages/replay-port/src/daemon-port}/session-test-outcome.ts (100%) diff --git a/packages/replay-port/package.json b/packages/replay-port/package.json index 64808197d2..7bb2c3a491 100644 --- a/packages/replay-port/package.json +++ b/packages/replay-port/package.json @@ -9,6 +9,7 @@ "@agent-device/ad-script": "workspace:*", "@agent-device/capture-kit": "workspace:*", "@agent-device/contracts": "workspace:*", + "@agent-device/host-kit": "workspace:*", "@agent-device/kernel": "workspace:*", "@agent-device/maestro": "workspace:*", "@agent-device/replay-test": "workspace:*", @@ -35,10 +36,38 @@ "types": "./src/daemon-port/session-replay-maestro-request.ts", "default": "./src/daemon-port/session-replay-maestro-request.ts" }, + "./session-replay-report-action": { + "types": "./src/daemon-port/session-replay-report-action.ts", + "default": "./src/daemon-port/session-replay-report-action.ts" + }, + "./session-replay-runtime-artifacts": { + "types": "./src/daemon-port/session-replay-runtime-artifacts.ts", + "default": "./src/daemon-port/session-replay-runtime-artifacts.ts" + }, + "./session-replay-suggestion-ranking": { + "types": "./src/daemon-port/session-replay-suggestion-ranking.ts", + "default": "./src/daemon-port/session-replay-suggestion-ranking.ts" + }, + "./session-replay-test-policy": { + "types": "./src/daemon-port/session-replay-test-policy.ts", + "default": "./src/daemon-port/session-replay-test-policy.ts" + }, + "./session-replay-trace": { + "types": "./src/daemon-port/session-replay-trace.ts", + "default": "./src/daemon-port/session-replay-trace.ts" + }, "./session-replay-vars": { "types": "./src/session-replay-vars.ts", "default": "./src/session-replay-vars.ts" }, + "./session-test-infrastructure": { + "types": "./src/daemon-port/session-test-infrastructure.ts", + "default": "./src/daemon-port/session-test-infrastructure.ts" + }, + "./session-test-outcome": { + "types": "./src/daemon-port/session-test-outcome.ts", + "default": "./src/daemon-port/session-test-outcome.ts" + }, "./target-classification": { "types": "./src/daemon-port/target-classification.ts", "default": "./src/daemon-port/target-classification.ts" diff --git a/src/daemon/replay/internal/__tests__/session-replay-runtime-artifacts.test.ts b/packages/replay-port/src/daemon-port/__tests__/session-replay-runtime-artifacts.test.ts similarity index 93% rename from src/daemon/replay/internal/__tests__/session-replay-runtime-artifacts.test.ts rename to packages/replay-port/src/daemon-port/__tests__/session-replay-runtime-artifacts.test.ts index 8c589c28a6..59ec9100bf 100644 --- a/src/daemon/replay/internal/__tests__/session-replay-runtime-artifacts.test.ts +++ b/packages/replay-port/src/daemon-port/__tests__/session-replay-runtime-artifacts.test.ts @@ -1,9 +1,10 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; +import os from 'node:os'; import { test } from 'vitest'; import { collectReplayActionArtifactPaths } from '../session-replay-runtime-artifacts.ts'; -import { mkdtempForTestSync } from '../../../../__tests__/test-utils/tmp-dir.ts'; +const mkdtempForTestSync = (prefix: string) => fs.mkdtempSync(path.join(os.tmpdir(), prefix)); test('collectReplayActionArtifactPaths includes existing failed action artifacts', () => { const root = mkdtempForTestSync('agent-device-replay-artifacts-'); diff --git a/src/daemon/replay/internal/__tests__/session-test-infrastructure.test.ts b/packages/replay-port/src/daemon-port/__tests__/session-test-infrastructure.test.ts similarity index 94% rename from src/daemon/replay/internal/__tests__/session-test-infrastructure.test.ts rename to packages/replay-port/src/daemon-port/__tests__/session-test-infrastructure.test.ts index 3649ce82a5..f709103b5c 100644 --- a/src/daemon/replay/internal/__tests__/session-test-infrastructure.test.ts +++ b/packages/replay-port/src/daemon-port/__tests__/session-test-infrastructure.test.ts @@ -1,8 +1,7 @@ import { test } from 'vitest'; import assert from 'node:assert/strict'; import { isReplayInfrastructureFailure } from '../session-test-infrastructure.ts'; -import { ALLOCATOR_CLAIM_MISSING } from '../../../device/device-claim-conflict.ts'; -import type { DaemonResponse } from '../../../daemon-request.ts'; +import type { DaemonResponse } from '@agent-device/kernel/contracts'; import type { ReplaySuiteTestResult } from '@agent-device/contracts/replay'; test('isReplayInfrastructureFailure accepts shared boot diagnostic reasons', () => { @@ -132,7 +131,7 @@ test('isReplayInfrastructureFailure does not retry a missing allocator-held clai code: 'COMMAND_FAILED', message: 'android device emulator-5554 is a managed identity with no allocator-held execution claim for this installation.', - details: { reason: ALLOCATOR_CLAIM_MISSING, retriable: false }, + details: { reason: 'allocator-claim-missing', retriable: false }, }, }; diff --git a/src/daemon/replay/internal/__tests__/session-test-outcome.test.ts b/packages/replay-port/src/daemon-port/__tests__/session-test-outcome.test.ts similarity index 96% rename from src/daemon/replay/internal/__tests__/session-test-outcome.test.ts rename to packages/replay-port/src/daemon-port/__tests__/session-test-outcome.test.ts index 93e2c71749..8d85e42703 100644 --- a/src/daemon/replay/internal/__tests__/session-test-outcome.test.ts +++ b/packages/replay-port/src/daemon-port/__tests__/session-test-outcome.test.ts @@ -1,6 +1,6 @@ import { expect, test } from 'vitest'; import { toReplayTestAttemptOutcome } from '../session-test-outcome.ts'; -import type { DaemonResponse } from '../../../daemon-request.ts'; +import type { DaemonResponse } from '@agent-device/kernel/contracts'; test('failed attempt outcome carries warnings from the error details (#2560)', () => { const response: DaemonResponse = { diff --git a/src/daemon/replay/internal/session-replay-report-action.ts b/packages/replay-port/src/daemon-port/session-replay-report-action.ts similarity index 100% rename from src/daemon/replay/internal/session-replay-report-action.ts rename to packages/replay-port/src/daemon-port/session-replay-report-action.ts diff --git a/src/daemon/replay/internal/session-replay-runtime-artifacts.ts b/packages/replay-port/src/daemon-port/session-replay-runtime-artifacts.ts similarity index 100% rename from src/daemon/replay/internal/session-replay-runtime-artifacts.ts rename to packages/replay-port/src/daemon-port/session-replay-runtime-artifacts.ts diff --git a/src/daemon/replay/internal/session-replay-suggestion-ranking.ts b/packages/replay-port/src/daemon-port/session-replay-suggestion-ranking.ts similarity index 100% rename from src/daemon/replay/internal/session-replay-suggestion-ranking.ts rename to packages/replay-port/src/daemon-port/session-replay-suggestion-ranking.ts diff --git a/src/daemon/replay/internal/session-replay-test-policy.ts b/packages/replay-port/src/daemon-port/session-replay-test-policy.ts similarity index 100% rename from src/daemon/replay/internal/session-replay-test-policy.ts rename to packages/replay-port/src/daemon-port/session-replay-test-policy.ts diff --git a/src/daemon/replay/internal/session-replay-trace.ts b/packages/replay-port/src/daemon-port/session-replay-trace.ts similarity index 100% rename from src/daemon/replay/internal/session-replay-trace.ts rename to packages/replay-port/src/daemon-port/session-replay-trace.ts diff --git a/src/daemon/replay/internal/session-test-infrastructure.ts b/packages/replay-port/src/daemon-port/session-test-infrastructure.ts similarity index 100% rename from src/daemon/replay/internal/session-test-infrastructure.ts rename to packages/replay-port/src/daemon-port/session-test-infrastructure.ts diff --git a/src/daemon/replay/internal/session-test-outcome.ts b/packages/replay-port/src/daemon-port/session-test-outcome.ts similarity index 100% rename from src/daemon/replay/internal/session-test-outcome.ts rename to packages/replay-port/src/daemon-port/session-test-outcome.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d13f54cd9f..47ca30ed72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -515,6 +515,9 @@ importers: '@agent-device/contracts': specifier: workspace:* version: link:../contracts + '@agent-device/host-kit': + specifier: workspace:* + version: link:../host-kit '@agent-device/kernel': specifier: workspace:* version: link:../kernel diff --git a/src/daemon/replay/internal/__tests__/session-test-suite-command-flag-policy.test.ts b/src/daemon/replay/internal/__tests__/session-test-suite-command-flag-policy.test.ts index adf046e675..f40e020471 100644 --- a/src/daemon/replay/internal/__tests__/session-test-suite-command-flag-policy.test.ts +++ b/src/daemon/replay/internal/__tests__/session-test-suite-command-flag-policy.test.ts @@ -19,7 +19,7 @@ import { splitReplayCommandRequest, } from '@agent-device/replay-port/replay-dispatch-envelope'; import type { ReplayTestCommand } from '@agent-device/replay-port/command-types'; -import { REPLAY_ONLY_TEST_FLAG_REJECTIONS } from '../session-replay-test-policy.ts'; +import { REPLAY_ONLY_TEST_FLAG_REJECTIONS } from '@agent-device/replay-port/session-replay-test-policy'; import { replayCommandFamily } from '../../../../commands/replay/index.ts'; import { mkdtempForTestSync } from '../../../../__tests__/test-utils/tmp-dir.ts'; import type { DaemonInvokeFn, DaemonRequest } from '../../../daemon-request.ts'; diff --git a/src/daemon/replay/internal/session-replay-action-runtime.ts b/src/daemon/replay/internal/session-replay-action-runtime.ts index 2af67a320d..3221deb61c 100644 --- a/src/daemon/replay/internal/session-replay-action-runtime.ts +++ b/src/daemon/replay/internal/session-replay-action-runtime.ts @@ -12,7 +12,7 @@ import { swipePayloadFromPositionals, } from '@agent-device/contracts/gesture-normalization'; import { buildDisplayPositionals } from '@agent-device/session-journal/session-event-action'; -import { appendReplayTraceEvent } from './session-replay-trace.ts'; +import { appendReplayTraceEvent } from '@agent-device/replay-port/session-replay-trace'; import { inferFillText, readRecordedInputVariableName } from '@agent-device/ad-script'; import { type DaemonResponse } from '@agent-device/kernel/contracts'; diff --git a/src/daemon/replay/internal/session-replay-divergence.ts b/src/daemon/replay/internal/session-replay-divergence.ts index 569cf44a6e..9d81ffaa2e 100644 --- a/src/daemon/replay/internal/session-replay-divergence.ts +++ b/src/daemon/replay/internal/session-replay-divergence.ts @@ -25,8 +25,8 @@ import type { import type { ReplayObservationEvidence } from '@agent-device/contracts/replay'; import { boundReplayDivergenceForSession } from './session-replay-divergence-publication.ts'; -import type { ReplayReportAction } from './session-replay-report-action.ts'; -import { rankAndDedupeReplaySuggestions } from './session-replay-suggestion-ranking.ts'; +import type { ReplayReportAction } from '@agent-device/replay-port/session-replay-report-action'; +import { rankAndDedupeReplaySuggestions } from '@agent-device/replay-port/session-replay-suggestion-ranking'; import { type ReplayDivergence, diff --git a/src/daemon/replay/internal/session-replay-heal.ts b/src/daemon/replay/internal/session-replay-heal.ts index 4ec9cea1de..c1b1277ed8 100644 --- a/src/daemon/replay/internal/session-replay-heal.ts +++ b/src/daemon/replay/internal/session-replay-heal.ts @@ -2,7 +2,7 @@ import path from 'node:path'; import { uniqueStrings } from '@agent-device/kernel/collections'; import { readSelectorExpression } from '@agent-device/selectors'; import { isTouchTargetCommand } from '@agent-device/ad-script'; -import type { ReplayReportAction } from './session-replay-report-action.ts'; +import type { ReplayReportAction } from '@agent-device/replay-port/session-replay-report-action'; /** * ADR 0012 decision 1 / migration step 6: `--update` retired as an actor — diff --git a/src/daemon/replay/internal/session-replay-maestro-failure.ts b/src/daemon/replay/internal/session-replay-maestro-failure.ts index f545111e80..831affec37 100644 --- a/src/daemon/replay/internal/session-replay-maestro-failure.ts +++ b/src/daemon/replay/internal/session-replay-maestro-failure.ts @@ -16,8 +16,8 @@ import type { ReplaySessionState, ReplaySessionStore, } from '@agent-device/replay-port/command-types'; -import type { ReplayReportAction } from './session-replay-report-action.ts'; -import { rankAndDedupeReplaySuggestions } from './session-replay-suggestion-ranking.ts'; +import type { ReplayReportAction } from '@agent-device/replay-port/session-replay-report-action'; +import { rankAndDedupeReplaySuggestions } from '@agent-device/replay-port/session-replay-suggestion-ranking'; import { buildReplayDivergenceSuggestionForNode, buildDivergenceScreen, diff --git a/src/daemon/replay/internal/session-replay-maestro-observer.ts b/src/daemon/replay/internal/session-replay-maestro-observer.ts index 89b7dded01..7acabf65e2 100644 --- a/src/daemon/replay/internal/session-replay-maestro-observer.ts +++ b/src/daemon/replay/internal/session-replay-maestro-observer.ts @@ -7,7 +7,7 @@ import type { import { AppError } from '@agent-device/kernel/errors'; import type { ReplayTestAttemptStepSink } from '@agent-device/replay-test'; import { stripUndefined } from '@agent-device/kernel/record'; -import { appendReplayTraceEvent } from './session-replay-trace.ts'; +import { appendReplayTraceEvent } from '@agent-device/replay-port/session-replay-trace'; export function createMaestroReplayObserver(params: { filePath: string; diff --git a/src/daemon/replay/internal/session-replay-runtime-engine-adapter.ts b/src/daemon/replay/internal/session-replay-runtime-engine-adapter.ts index 3c6db1b2cc..0cfbf22d3b 100644 --- a/src/daemon/replay/internal/session-replay-runtime-engine-adapter.ts +++ b/src/daemon/replay/internal/session-replay-runtime-engine-adapter.ts @@ -19,7 +19,7 @@ import { withReplayFailureDiagnostics } from './session-replay-runtime-failure.t import { invokeReplayAction } from './session-replay-action-runtime.ts'; import type { AdReplayStepFailure, AdReplayStepRuntime } from '@agent-device/ad-replay'; -import { collectReplayActionArtifactPaths } from './session-replay-runtime-artifacts.ts'; +import { collectReplayActionArtifactPaths } from '@agent-device/replay-port/session-replay-runtime-artifacts'; import { applyReplayDispatchGuard, classifyReplayDispatchFailure, diff --git a/src/daemon/replay/internal/session-replay-video-recording.ts b/src/daemon/replay/internal/session-replay-video-recording.ts index fd93dddef6..02b80f75bd 100644 --- a/src/daemon/replay/internal/session-replay-video-recording.ts +++ b/src/daemon/replay/internal/session-replay-video-recording.ts @@ -4,7 +4,7 @@ import type { DaemonWireRequest } from '@agent-device/contracts/command'; import { emitDiagnostic } from '@agent-device/host-kit/diagnostics'; import { sleep } from '@agent-device/host-kit/retry'; -import { collectReplayActionArtifactPaths } from './session-replay-runtime-artifacts.ts'; +import { collectReplayActionArtifactPaths } from '@agent-device/replay-port/session-replay-runtime-artifacts'; import type { ReplayRecordVideoRequest, ReplaySessionStore, diff --git a/src/daemon/replay/internal/test-command.ts b/src/daemon/replay/internal/test-command.ts index 0dee0c807a..c8cf3450fd 100644 --- a/src/daemon/replay/internal/test-command.ts +++ b/src/daemon/replay/internal/test-command.ts @@ -19,7 +19,7 @@ import { type ReplayTestSuiteRequest, } from '@agent-device/replay-test'; import { runReplayCommand } from './native-command.ts'; -import { collectReplayActionArtifactPaths } from './session-replay-runtime-artifacts.ts'; +import { collectReplayActionArtifactPaths } from '@agent-device/replay-port/session-replay-runtime-artifacts'; import { AppError, asAppError } from '@agent-device/kernel/errors'; import { emitRequestProgress, @@ -37,13 +37,16 @@ import { buildReplayTestShardTargetResolver, readReplayTestShardSelection, } from './session-test-shard-devices.ts'; -import { toReplayTestAttemptOutcome, toReplayTestFinalizeFailure } from './session-test-outcome.ts'; +import { + toReplayTestAttemptOutcome, + toReplayTestFinalizeFailure, +} from '@agent-device/replay-port/session-test-outcome'; import { buildReplayTestVideoOpenLifecycle, finalizeReplayTestVideoRecording, startReplayTestVideoRecordingIfReady, } from './session-replay-video-recording.ts'; -import { REPLAY_ONLY_TEST_FLAG_REJECTIONS } from './session-replay-test-policy.ts'; +import { REPLAY_ONLY_TEST_FLAG_REJECTIONS } from '@agent-device/replay-port/session-replay-test-policy'; import { errorResponse, type DaemonResponse, From ebd8126cfd95a784c7f750fa722b3eed8fca6357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Mon, 28 Sep 2026 13:27:55 +0200 Subject: [PATCH 2/3] test(replay): use package scratch helper in moved artifact test --- .../__tests__/session-replay-runtime-artifacts.test.ts | 3 +-- packages/replay-port/src/tmp-dir.fixtures.ts | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 packages/replay-port/src/tmp-dir.fixtures.ts diff --git a/packages/replay-port/src/daemon-port/__tests__/session-replay-runtime-artifacts.test.ts b/packages/replay-port/src/daemon-port/__tests__/session-replay-runtime-artifacts.test.ts index 59ec9100bf..fce9c1ec73 100644 --- a/packages/replay-port/src/daemon-port/__tests__/session-replay-runtime-artifacts.test.ts +++ b/packages/replay-port/src/daemon-port/__tests__/session-replay-runtime-artifacts.test.ts @@ -1,10 +1,9 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; -import os from 'node:os'; import { test } from 'vitest'; import { collectReplayActionArtifactPaths } from '../session-replay-runtime-artifacts.ts'; -const mkdtempForTestSync = (prefix: string) => fs.mkdtempSync(path.join(os.tmpdir(), prefix)); +import { mkdtempForTestSync } from '../../tmp-dir.fixtures.ts'; test('collectReplayActionArtifactPaths includes existing failed action artifacts', () => { const root = mkdtempForTestSync('agent-device-replay-artifacts-'); diff --git a/packages/replay-port/src/tmp-dir.fixtures.ts b/packages/replay-port/src/tmp-dir.fixtures.ts new file mode 100644 index 0000000000..cc9fbd664e --- /dev/null +++ b/packages/replay-port/src/tmp-dir.fixtures.ts @@ -0,0 +1,8 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; + +/** The unit suite redirects TMPDIR to a per-run directory and removes it after each worker. */ +export function mkdtempForTestSync(prefix: string): string { + return fs.mkdtempSync(path.join(os.tmpdir(), prefix)); +} From b0948dda642a3329a3d3e8b2b356c702248a083b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Mon, 28 Sep 2026 15:50:13 +0200 Subject: [PATCH 3/3] test(replay): document package-local scratch fixture --- packages/replay-port/src/tmp-dir.fixtures.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/replay-port/src/tmp-dir.fixtures.ts b/packages/replay-port/src/tmp-dir.fixtures.ts index cc9fbd664e..cf94d60647 100644 --- a/packages/replay-port/src/tmp-dir.fixtures.ts +++ b/packages/replay-port/src/tmp-dir.fixtures.ts @@ -2,7 +2,8 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; -/** The unit suite redirects TMPDIR to a per-run directory and removes it after each worker. */ +/** The unit suite redirects TMPDIR to a per-run directory removed after all workers finish. */ +// fallow-ignore-next-line code-duplication export function mkdtempForTestSync(prefix: string): string { return fs.mkdtempSync(path.join(os.tmpdir(), prefix)); }