Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ under `contracts/fixtures/`.
## Runtime and diagnostics seams

Diagnostics use `@agent-device/host-kit/diagnostics`. Request diagnostics belong in the session request log;
session artifact paths come from `packages/daemon/src/core/session-artifact-paths.ts`. App/device logs remain in `app.log`;
session artifact paths come from `packages/daemon-contracts/src/session-artifact-paths.ts`. App/device logs remain in `app.log`;
Apple runner and xcodebuild output remains in `runner.log`.

Normalize failures with `normalizeError` and preserve `hint`, `diagnosticId`, `logPath`, and typed
Expand Down
2 changes: 2 additions & 0 deletions docs/adr/0032-layering-graph-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ finding. Clean tree: custom 0, depcruise 10 (3 R6 survivors, 7 recorded R78 edge
| R14 non-TS file under `src/utils` | flag | ✓ | ✗ | ✗ |
| R71 file under `src/replay` | flag | ✓ | ✓ | ✓ generic |

_R78 no longer records edges: the client's direct imports are a flat ban in `LOGICAL_MODULE_POLICIES` (R10), and R78 now walks reachability from the CLI entries, so the "unrecorded" and "stale" rows describe the spike at its date, not the rules as shipped._

Custom prints `file:line`, rule id and hint (`report()` in `scripts/layering/check.ts`); depcruise
the rule name and an authored comment, without a line. "Generic" means a fallow zone pair
(`core → cli`) and a docs link, with no rule id or hint; R14 and R71 surface as "matches no zone".
Expand Down
10 changes: 6 additions & 4 deletions docs/adr/0033-collocation-decision-table.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ already owns the operations behind the vocabulary.
| `src/diagnostics.ts` | keep | The diagnostics seam every zone imports (AGENTS.md mandates it); highest fan-in in the repo is what a substrate looks like, not misplacement. |
| `src/internal/request-cancel.ts` | keep | Request-cancellation mechanics AGENTS.md routes to `host-kit/request`; internal beside its facade is already the collocation. |
| `src/request.ts` | keep | Cross-layer request contracts AGENTS.md names as a host-kit seam; consumers span every zone. |
| `src/session-paths.ts` | keep | Session-artifact path grammar the daemon, cli, and platforms all resolve; AGENTS.md keeps the path *policy* in `packages/daemon/src/core/session-artifact-paths.ts`, which consumes this. |
| `src/session-paths.ts` | keep | Session-artifact path grammar the daemon, cli, and platforms all resolve; AGENTS.md keeps the path *policy* in `packages/daemon-contracts/src/session-artifact-paths.ts`, which consumes this. |

### kernel

Expand Down Expand Up @@ -118,7 +118,7 @@ already owns the operations behind the vocabulary.

| File | Decision | Reason |
| --- | --- | --- |
| `src/cli/commands/device-release.ts` | keep | Command surface belongs in `cli` by the folder spine; the daemon device-claim modules it reuses are already extracted, so what remains is thin daemonless-CLI composition. |
| `src/cli/commands/device-release.ts` | moved | Now `src/platform-runtime-claim-recovery.ts`: the daemonless stale release composes the shared reconciler with the root gateway, and the CLI process may reach no daemon module (R78), so it sits beside the claim-recovery gateway in the platform-runtime composition. |

### (root)

Expand All @@ -133,8 +133,9 @@ the whole `daemon-contracts` zone has moved into `packages/daemon-contracts` —
package derive the zone, so every moved file's `ROOT_MODULE_ZONES` row is gone.
The daemon ⇄ client shared files that remain here (`provider-credential-fingerprint.ts`) trace
to #2559, which relocated the shared contracts **to the process root** so the client stops
importing `packages/daemon/src/` at all; the `daemon-client` logical-module policy bans every edge from `src/daemon-client/` into
`packages/daemon/src/`, and #3288's `daemon-contracts`
importing `packages/daemon/src/` at all; the `client-side` logical-module policy bans every edge from the CLI, typed client, remote transports and
`src/daemon-client/` into `packages/daemon/src/`, R78 bans the hop through any root module the CLI
process reaches, and #3288's `daemon-contracts`
zone was the declaration of that shared-below-both ownership — the extraction makes it
physical, with R11 and the package's dependency edges holding the same boundary. Rows on
path-keyed evidence alone (the `platform-runtime-*` seams) say "retain for this batch": a guard's
Expand All @@ -144,6 +145,7 @@ optimal.
| File | Decision | Reason |
| --- | --- | --- |
| `src/daemon-diagnostics-scope.ts` | moved → `packages/daemon-contracts/src/daemon-diagnostics-scope.ts` ([#3297](https://github.com/callstack/agent-device/pull/3297) into `src/daemon-contracts/`, then the package extraction) | The whole `daemon-contracts` zone now lives as `packages/daemon-contracts`, so the package directory derives the zone and the per-file `ROOT_MODULE_ZONES` row stays deleted; an internal rename needs no ownership-table edit. |
| `packages/daemon-contracts/src/device-claim-recovery.ts` | keep (shared-below-both) | The owner-scoped claim reconciler runs durable-resource recovery for a dead owner's state dir; the daemon runs it at `open` and startup and the daemonless `device release --stale` runs it too, so it lives below both. Runtime recovery may live in `daemon-contracts` only when both sides run it and root composition supplies just the gateway (ADR 0019 section 1); the package depends on `capture-kit`, which cannot depend back on it (a project-reference cycle through `selectors`). |
| `src/daemon-policy-file.ts` | moved → `packages/daemon-contracts/src/daemon-policy-file.ts` | Daemon ⇄ client shared contract from #2559, declared `daemon-contracts` by #3288 — the shared-below-both ownership the spine needs, since daemon-client (rank 5) dynamically imports it. The extraction makes that placement physical: the package sits below both sides and its manifest exports own the boundary. |
| `src/daemon.ts` | keep | Entry point (`internal/daemon` bundle entry); composition roots stay in root by umbrella §4. |
| `src/platform-runtime-apple-runner-owner.ts` | retain for this batch | `platform-runtime-*` composition seam: R13's exact-importer rule and the ADR 0022 R76 inventory key on its path, so relocation requires coordinated ownership changes to both declarations — the rules constrain the move, they do not independently prove the placement. |
Expand Down
146 changes: 79 additions & 67 deletions packages/capture-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@
"types": "./src/snapshot/android-replacement-surface-occlusion.ts",
"default": "./src/snapshot/android-replacement-surface-occlusion.ts"
},
"./app-log-durable-resource": {
"types": "./src/capture-admission/app-log-durable-resource.ts",
"default": "./src/capture-admission/app-log-durable-resource.ts"
},
"./app-log-files": {
"types": "./src/capture-admission/app-log-files.ts",
"default": "./src/capture-admission/app-log-files.ts"
},
"./app-log-resource-recovery": {
"types": "./src/capture-admission/app-log-resource-recovery.ts",
"default": "./src/capture-admission/app-log-resource-recovery.ts"
},
"./audio-probe-admission-ledger": {
"types": "./src/capture-admission/audio-probe-admission-ledger.ts",
"default": "./src/capture-admission/audio-probe-admission-ledger.ts"
Expand All @@ -38,6 +50,10 @@
"types": "./src/capture-admission/audio-probe-session-resource.ts",
"default": "./src/capture-admission/audio-probe-session-resource.ts"
},
"./durable-capture": {
"types": "./src/durable-capture/index.ts",
"default": "./src/durable-capture/index.ts"
},
"./durable-capture-admission-ledger": {
"types": "./src/capture-admission/durable-capture-admission-ledger.ts",
"default": "./src/capture-admission/durable-capture-admission-ledger.ts"
Expand All @@ -50,66 +66,14 @@
"types": "./src/capture-admission/durable-capture-runtime-recovery.ts",
"default": "./src/capture-admission/durable-capture-runtime-recovery.ts"
},
"./observe-until": {
"types": "./src/observe-until.ts",
"default": "./src/observe-until.ts"
},
"./perf-capture-admission-ledger": {
"types": "./src/capture-admission/perf-capture-admission-ledger.ts",
"default": "./src/capture-admission/perf-capture-admission-ledger.ts"
},
"./perf-capture-recovery": {
"types": "./src/capture-admission/perf-capture-resource-recovery.ts",
"default": "./src/capture-admission/perf-capture-resource-recovery.ts"
},
"./perf-capture-resource-store": {
"types": "./src/capture-admission/perf-capture-resource-store.ts",
"default": "./src/capture-admission/perf-capture-resource-store.ts"
},
"./perf-capture-session-resource": {
"types": "./src/capture-admission/perf-capture-session-resource.ts",
"default": "./src/capture-admission/perf-capture-session-resource.ts"
},
"./perf-runtime-plan": {
"types": "./src/capture-admission/perf-runtime-plan.ts",
"default": "./src/capture-admission/perf-runtime-plan.ts"
},
"./screen-recording-admission-ledger": {
"types": "./src/capture-admission/screen-recording-admission-ledger.ts",
"default": "./src/capture-admission/screen-recording-admission-ledger.ts"
},
"./screen-recording-recovery": {
"types": "./src/capture-admission/screen-recording-resource-recovery.ts",
"default": "./src/capture-admission/screen-recording-resource-recovery.ts"
},
"./screen-recording-resource-store": {
"types": "./src/capture-admission/screen-recording-resource-store.ts",
"default": "./src/capture-admission/screen-recording-resource-store.ts"
},
"./screen-recording-session-resource": {
"types": "./src/capture-admission/screen-recording-session-resource.ts",
"default": "./src/capture-admission/screen-recording-session-resource.ts"
},
"./screen-recording-stop-recovery": {
"types": "./src/capture-admission/screen-recording-stop-recovery.ts",
"default": "./src/capture-admission/screen-recording-stop-recovery.ts"
},
"./durable-capture": {
"types": "./src/durable-capture/index.ts",
"default": "./src/durable-capture/index.ts"
"./durable-capture/session-binding": {
"types": "./src/durable-capture/session-binding.ts",
"default": "./src/durable-capture/session-binding.ts"
},
"./durable-json": {
"types": "./src/durable-json.ts",
"default": "./src/durable-json.ts"
},
"./recording-stop-sequence": {
"types": "./src/recording/stop-sequence.ts",
"default": "./src/recording/stop-sequence.ts"
},
"./screen-recording-transport": {
"types": "./src/screen-recording-transport.ts",
"default": "./src/screen-recording-transport.ts"
},
"./ios-node-interpretation": {
"types": "./src/ios-node-interpretation.ts",
"default": "./src/ios-node-interpretation.ts"
Expand All @@ -122,10 +86,6 @@
"types": "./src/ios-snapshot-engine/index.ts",
"default": "./src/ios-snapshot-engine/index.ts"
},
"./ios-snapshot-tree": {
"types": "./src/ios-snapshot-engine/tree.ts",
"default": "./src/ios-snapshot-engine/tree.ts"
},
"./ios-snapshot-planning": {
"types": "./src/ios-snapshot-planning.ts",
"default": "./src/ios-snapshot-planning.ts"
Expand All @@ -134,10 +94,38 @@
"types": "./src/snapshot/ios-snapshot-runtime.ts",
"default": "./src/snapshot/ios-snapshot-runtime.ts"
},
"./ios-snapshot-tree": {
"types": "./src/ios-snapshot-engine/tree.ts",
"default": "./src/ios-snapshot-engine/tree.ts"
},
"./mobile-snapshot-semantics": {
"types": "./src/mobile-snapshot-semantics.ts",
"default": "./src/mobile-snapshot-semantics.ts"
},
"./observe-until": {
"types": "./src/observe-until.ts",
"default": "./src/observe-until.ts"
},
"./perf-capture-admission-ledger": {
"types": "./src/capture-admission/perf-capture-admission-ledger.ts",
"default": "./src/capture-admission/perf-capture-admission-ledger.ts"
},
"./perf-capture-recovery": {
"types": "./src/capture-admission/perf-capture-resource-recovery.ts",
"default": "./src/capture-admission/perf-capture-resource-recovery.ts"
},
"./perf-capture-resource-store": {
"types": "./src/capture-admission/perf-capture-resource-store.ts",
"default": "./src/capture-admission/perf-capture-resource-store.ts"
},
"./perf-capture-session-resource": {
"types": "./src/capture-admission/perf-capture-session-resource.ts",
"default": "./src/capture-admission/perf-capture-session-resource.ts"
},
"./perf-runtime-plan": {
"types": "./src/capture-admission/perf-runtime-plan.ts",
"default": "./src/capture-admission/perf-runtime-plan.ts"
},
"./png": {
"types": "./src/png.ts",
"default": "./src/png.ts"
Expand Down Expand Up @@ -190,14 +178,14 @@
"types": "./src/recording/recording-facts.ts",
"default": "./src/recording/recording-facts.ts"
},
"./recording-mp4-fixtures": {
"types": "./src/recording/mp4.fixtures.ts",
"default": "./src/recording/mp4.fixtures.ts"
},
"./recording-mp4-duration": {
"types": "./src/recording/mp4-duration.ts",
"default": "./src/recording/mp4-duration.ts"
},
"./recording-mp4-fixtures": {
"types": "./src/recording/mp4.fixtures.ts",
"default": "./src/recording/mp4.fixtures.ts"
},
"./recording-output-path": {
"types": "./src/recording/output-path.ts",
"default": "./src/recording/output-path.ts"
Expand All @@ -206,6 +194,10 @@
"types": "./src/recording/overlay.ts",
"default": "./src/recording/overlay.ts"
},
"./recording-stop-sequence": {
"types": "./src/recording/stop-sequence.ts",
"default": "./src/recording/stop-sequence.ts"
},
"./recording-telemetry": {
"types": "./src/recording/telemetry.ts",
"default": "./src/recording/telemetry.ts"
Expand All @@ -218,6 +210,30 @@
"types": "./src/snapshot/snapshot-presentation/repeated-nav-subtree.ts",
"default": "./src/snapshot/snapshot-presentation/repeated-nav-subtree.ts"
},
"./screen-recording-admission-ledger": {
"types": "./src/capture-admission/screen-recording-admission-ledger.ts",
"default": "./src/capture-admission/screen-recording-admission-ledger.ts"
},
"./screen-recording-recovery": {
"types": "./src/capture-admission/screen-recording-resource-recovery.ts",
"default": "./src/capture-admission/screen-recording-resource-recovery.ts"
},
"./screen-recording-resource-store": {
"types": "./src/capture-admission/screen-recording-resource-store.ts",
"default": "./src/capture-admission/screen-recording-resource-store.ts"
},
"./screen-recording-session-resource": {
"types": "./src/capture-admission/screen-recording-session-resource.ts",
"default": "./src/capture-admission/screen-recording-session-resource.ts"
},
"./screen-recording-stop-recovery": {
"types": "./src/capture-admission/screen-recording-stop-recovery.ts",
"default": "./src/capture-admission/screen-recording-stop-recovery.ts"
},
"./screen-recording-transport": {
"types": "./src/screen-recording-transport.ts",
"default": "./src/screen-recording-transport.ts"
},
"./screenshot-density": {
"types": "./src/screenshot-density.ts",
"default": "./src/screenshot-density.ts"
Expand Down Expand Up @@ -325,10 +341,6 @@
"./touch-reference-frame": {
"types": "./src/snapshot/touch-reference-frame.ts",
"default": "./src/snapshot/touch-reference-frame.ts"
},
"./durable-capture/session-binding": {
"types": "./src/durable-capture/session-binding.ts",
"default": "./src/durable-capture/session-binding.ts"
}
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { AppLogCompletion, AppLogLiveHandle } from '@agent-device/contracts/app-log-runtime';
import { createDurableCaptureResource } from './durable-capture-resource.ts';
import { appLogResourceStore } from './app-log-resource-store.ts';

export const appLogDurableResource = createDurableCaptureResource<
'app-log',
AppLogLiveHandle,
AppLogCompletion
>({
resourceKind: 'app-log',
displayName: 'app-log',
store: appLogResourceStore,
completionMetadata: (completion) => ({
backend: completion.backend,
outputPath: completion.outputPath,
completedAt: completion.completedAt,
}),
failedFinishPolicy: 'dispose-on-failed-finish',
messages: {
noActive: 'no app log stream active',
cleanupPendingHint:
'Keep app-log.resource.json and retry cleanup through its exact runtime owner.',
},
});

export { appLogResourceStore };
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import {
import type {
DurableCaptureRecoveryControl,
DurableCaptureRecoverySummary,
} from '@agent-device/capture-kit/durable-capture';
} from '../durable-capture/index.ts';
import type { ResourceDiagnostic } from '@agent-device/host-kit/diagnostics';
import { appLogDurableResource } from './app-log-session-resource.ts';
import { acquireExactDurableCaptureRecoveryControl } from '@agent-device/capture-kit/durable-capture-runtime-recovery';
import { appLogDurableResource } from './app-log-durable-resource.ts';
import { acquireExactDurableCaptureRecoveryControl } from './durable-capture-runtime-recovery.ts';

const appLogRecoveryUse = defineUse({
required: ['appLogReattach', 'appLogCleanup'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import fs from 'node:fs';
import path from 'node:path';
import { expect, test } from 'vitest';
import { localRuntimeOwner } from '@agent-device/contracts/platform-runtime';
import { createDurableResourceEnvelope } from '@agent-device/capture-kit';
import { mkdtempForTestSync } from '@agent-device/test-kit/tmp-dir';
import { appLogResourceStore } from '../app-log-resource-store.ts';
import { createDurableResourceEnvelope } from '../durable-resource-envelope.ts';
import { mkdtempForTestSync } from '../tmp-dir.fixtures.ts';
import { appLogResourceStore } from './app-log-resource-store.ts';

test('app-log resource records publish atomically with owner-only permissions', () => {
const resourcePath = appLogResourceStore.resolvePath(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createDurableCaptureResourceStore } from '@agent-device/capture-kit/durable-capture';
import { createDurableCaptureResourceStore } from '../durable-capture/index.ts';

export const appLogResourceStore = createDurableCaptureResourceStore({
resourceKind: 'app-log',
Expand Down
Loading
Loading