From b4d9a73c8ab4e0a8b9ed7ed9491049771561a4be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 1 Oct 2026 13:54:45 +0200 Subject: [PATCH 1/2] test(daemon): pin rethrow of an unnormalized failure without a recording effect --- .../__tests__/request-dispatch-disclosure.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/daemon/__tests__/request-dispatch-disclosure.test.ts b/src/daemon/__tests__/request-dispatch-disclosure.test.ts index 34f645ab94..9ecc449a4b 100644 --- a/src/daemon/__tests__/request-dispatch-disclosure.test.ts +++ b/src/daemon/__tests__/request-dispatch-disclosure.test.ts @@ -12,6 +12,7 @@ vi.mock('@agent-device/platform-apple/runner/operations', async (importOriginal) vi.mock('../device/device-ready.ts', () => ({ ensureDeviceReady: vi.fn(async () => {}) })); import { AppError } from '@agent-device/kernel/errors'; +import { resolveCommandRecordingEffect } from '@agent-device/command-registry/registry'; import type { AndroidObservationAdapter } from '@agent-device/contracts/android-observation'; import type { DeviceRuntimeGateway, RuntimeFacts } from '@agent-device/contracts/platform-runtime'; import type { PlatformRuntimeOperations } from '@agent-device/contracts/platform-runtime-operations'; @@ -29,6 +30,8 @@ import { import { makeSessionStore } from '../../__tests__/test-utils/store-factory.ts'; import { mkdtempForTestSync } from '../../__tests__/test-utils/tmp-dir.ts'; import type { DaemonRequest } from '../daemon-request.ts'; +import { discloseRequestDispatch } from '../request-dispatch-disclosure.ts'; +import { createRequestDispatchLedger } from '../request-dispatch-ledger.ts'; import { LeaseRegistry } from '../lease-registry.ts'; import type { SessionState } from '../session-state.ts'; import { clearAndroidObservationFixture } from './android-observation-fixture.ts'; @@ -355,3 +358,15 @@ for (const row of ROWS) { }); }); } + +test('a command without a declared recording effect rethrows its failure unnormalized', async () => { + const req: DaemonRequest = { token: 't', session: SESSION, command: 'devices', positionals: [] }; + assert.equal(resolveCommandRecordingEffect(req), undefined); + const thrown = { reason: 'not an Error' }; + await assert.rejects( + discloseRequestDispatch(req, createRequestDispatchLedger(), async () => { + throw thrown; + }), + (error: unknown) => error === thrown, + ); +}); From a1c38d56d574b61e7c8fe048270e217a9ac8d758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 1 Oct 2026 13:54:47 +0200 Subject: [PATCH 2/2] refactor(daemon): apply one dispatch-disclosure rule to returned and thrown failures --- src/daemon/request-dispatch-disclosure.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/daemon/request-dispatch-disclosure.ts b/src/daemon/request-dispatch-disclosure.ts index 51a7be066f..8fef8ecaeb 100644 --- a/src/daemon/request-dispatch-disclosure.ts +++ b/src/daemon/request-dispatch-disclosure.ts @@ -3,8 +3,6 @@ import { asAppError, detailsAfterDispatchedSteps, discloseDispatch, - discloseDispatchAfterSteps, - discloseUnclassifiedDispatch, type ErrorWireDetails, } from '@agent-device/kernel/errors'; import { resolveCommandRecordingEffect } from '@agent-device/command-registry/registry'; @@ -35,13 +33,10 @@ export async function discloseRequestDispatch 0) { - discloseDispatchAfterSteps(failure, ledger.dispatchedSteps); - throw failure; - } - if (effect === undefined) throw error; - if (effect === 'observes-app') throw discloseDispatch(failure, 'no'); - throw discloseUnclassifiedDispatch(failure, 'unknown'); + const details = disclosedDetails(effect, failure.details, ledger); + if (details === failure.details) throw error; + failure.details = details; + throw failure; } }