Skip to content

Commit 8e8eeb2

Browse files
authored
refactor(snapshot): drop the bridge truncation dimension; stop promising --scope for depth caps (#2511)
The dimension and limit the bridge adapter inferred for a cut capture had no renderer and one consumer, the comparison-identity string, where the kind alone gives the same comparability. The runner never produced them. The depth-cap warning suggested --scope to read deeper content; on iOS scope narrows presentation and acquisition stays scope-blind.
1 parent 1527146 commit 8e8eeb2

9 files changed

Lines changed: 22 additions & 62 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
had no disclosure at all, so an agent read a screen missing its footer, tab bar, or the items
88
after a long list as complete — the backends walk the tree in document order, so what falls
99
off is what comes last, on screen or not. One shared warning renders from the shared flag; the
10-
limit and dimension stay backend-side.
10+
limit and dimension stay backend-side. The depth-cap warning no longer suggests `--scope` as a
11+
way to read deeper: on iOS, scope narrows the presented view and acquisition stays scope-blind.
1112
- Changed: the iOS Simulator AX bridge caps a capture at 5000 nodes, up from 1500, the Android
1213
helper's bound. Measured on a synthetic 600-row screen, acquisition time did not move with the
1314
cap (the native read fetches the whole tree; the cap only stops conversion) while the 1500 cut

packages/capture-kit/src/ios-snapshot-engine/engine.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ function acquisition(
377377
truncated: false,
378378
viewport: { kind: 'reported', rect: viewport },
379379
lineage: { targetId: 'simulator-1', generation: 'generation-1' },
380-
residue: [{ kind: 'truncated', dimension: 'payload', limit: 2000 }],
380+
residue: [{ kind: 'truncated' }],
381381
};
382382
}
383383

@@ -391,7 +391,7 @@ function validationFacts(request: IosSnapshotRequest): IosSnapshotValidationFact
391391
viewport: { kind: 'reported', rect: viewport },
392392
hittability: { kind: 'available' },
393393
lineage: { targetId: 'simulator-1', generation: 'generation-1' },
394-
residue: [{ kind: 'truncated', dimension: 'payload', limit: 2000 }],
394+
residue: [{ kind: 'truncated' }],
395395
};
396396
}
397397

packages/capture-kit/src/ios-snapshot-planning.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ test('comparison identity rejects every identity axis and residue mismatch', ()
117117
{ ...base, intent: 'surface-observation' },
118118
{ ...base, lineage: { targetId: 'simulator-1', generation: 'generation-2' } },
119119
{ ...base, presentationKey: { ...base.presentationKey, depth: 1 } },
120-
{ ...base, residue: [{ kind: 'truncated', dimension: 'nodes' }] },
120+
{ ...base, residue: [{ kind: 'truncated' }] },
121121
];
122122
assert.equal(areIosSnapshotComparisonIdentitiesEqual(base, { ...base }), true);
123123
assert.equal(

packages/capture-kit/src/ios-snapshot-planning.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,7 @@ function residueIdentity(residue: IosAcquisitionResidue): string {
126126
case 'missing-viewport':
127127
return JSON.stringify({ kind: residue.kind, reason: residue.reason });
128128
case 'truncated':
129-
return JSON.stringify({
130-
kind: residue.kind,
131-
dimension: residue.dimension,
132-
limit: residue.limit,
133-
});
129+
return JSON.stringify({ kind: residue.kind });
134130
case 'stale-generation':
135131
return JSON.stringify({
136132
kind: residue.kind,

packages/capture-kit/src/snapshot/snapshot-presentation/quality-warnings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ function appAccessibilityDefectWarning(verdict: SnapshotQualityVerdict): string[
8484
function depthWarning(verdict: SnapshotQualityVerdict): string[] {
8585
if (verdict.effectiveDepth === undefined) return [];
8686
return [
87-
`Some deeper accessibility nodes were omitted; this tree is capped at depth ${verdict.effectiveDepth}. Re-run with --depth ${verdict.effectiveDepth} --scope <container> only if you need deeper content.`,
87+
`Some deeper accessibility nodes were omitted; the accessibility backend capped this tree at depth ${verdict.effectiveDepth}. Navigate so the content you need sits higher in the tree, and use screenshot as visual truth for the rest.`,
8888
];
8989
}
9090

packages/capture-kit/src/snapshot/snapshot-presentation/warnings.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ test('penalty-deferred recovered captures suppress the fallback warning but keep
3535
);
3636

3737
assert.deepEqual(warnings, [
38-
'Some deeper accessibility nodes were omitted; this tree is capped at depth 56. Re-run with --depth 56 --scope <container> only if you need deeper content.',
38+
'Some deeper accessibility nodes were omitted; the accessibility backend capped this tree at depth 56. Navigate so the content you need sits higher in the tree, and use screenshot as visual truth for the rest.',
3939
]);
4040
});
4141

@@ -53,7 +53,7 @@ test('non-presentation recovery keeps the generic warning for the same reason te
5353

5454
assert.deepEqual(warnings, [
5555
'Detected an overly complex or slow accessibility tree. Fell back to the private-ax snapshot backend. It is OK to continue; use --json to inspect snapshotQuality.reason if you need recovery details.',
56-
'Some deeper accessibility nodes were omitted; this tree is capped at depth 56. Re-run with --depth 56 --scope <container> only if you need deeper content.',
56+
'Some deeper accessibility nodes were omitted; the accessibility backend capped this tree at depth 56. Navigate so the content you need sits higher in the tree, and use screenshot as visual truth for the rest.',
5757
]);
5858
});
5959

@@ -71,7 +71,7 @@ test('presentation failures identify a runner bug and preserve composed warnings
7171

7272
assert.deepEqual(warnings, [
7373
'Agent Device could not safely present the captured accessibility tree and fell back to the private-ax snapshot backend. This is an Agent Device runner bug, not an app accessibility-tree issue. Use screenshot as visual truth and report snapshotQuality.reason with the screenshot.',
74-
'Some deeper accessibility nodes were omitted; this tree is capped at depth 56. Re-run with --depth 56 --scope <container> only if you need deeper content.',
74+
'Some deeper accessibility nodes were omitted; the accessibility backend capped this tree at depth 56. Navigate so the content you need sits higher in the tree, and use screenshot as visual truth for the rest.',
7575
]);
7676
});
7777

packages/contracts/src/ios-snapshot.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ export type IosHittabilityEvidence =
114114
}>;
115115

116116
export type IosProviderPrunedField = 'nodes' | 'depth' | 'scope' | 'interactive-only';
117-
export type IosTruncationDimension = 'nodes' | 'depth' | 'payload';
118117

119118
export type IosAcquisitionResidue =
120119
| Readonly<{
@@ -127,8 +126,6 @@ export type IosAcquisitionResidue =
127126
}>
128127
| Readonly<{
129128
kind: 'truncated';
130-
dimension: IosTruncationDimension;
131-
limit?: number;
132129
}>
133130
| Readonly<{
134131
kind: 'stale-generation';

packages/platform-apple/src/snapshot-source/adapter.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,7 @@ test('the Simulator AX source returns raw acquisition facts and discloses unsupp
7878
});
7979
assert.equal(rawDepthOne.stage, 'acquired');
8080
assert.equal(fixture.requestedDepths.at(-1), 1);
81-
assert.ok(
82-
rawDepthOne.acquisition.residue.some(
83-
(item) => item.kind === 'truncated' && item.dimension === 'depth',
84-
),
85-
);
81+
assert.ok(rawDepthOne.acquisition.residue.some((item) => item.kind === 'truncated'));
8682

8783
fixture.responsePid = 999;
8884
const outcome = await source.acquire({

packages/platform-apple/src/snapshot-source/adapter.ts

Lines changed: 11 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { AppError } from '@agent-device/kernel/errors';
2-
import type { CaptureHint, IosSnapshotAcquisition } from '@agent-device/contracts/ios-snapshot';
2+
import type {
3+
CaptureHint,
4+
IosSnapshotAcquisition,
5+
IosViewportEvidence,
6+
} from '@agent-device/contracts/ios-snapshot';
37
import { ensureSnapshotBridgeBinary } from './cache.ts';
48
import { createSnapshotSourceDeadline, remainingSnapshotSourceMs } from './deadline.ts';
59
import { AcceptedDepthHints, type DepthHintDecision } from './depth-hints.ts';
@@ -94,13 +98,7 @@ export function createSimulatorSnapshotSource(
9498
deadline,
9599
});
96100
remainingSnapshotSourceMs(deadline, 'snapshot-decode-deadline');
97-
const acquisition = createAcquisition(
98-
request.hint,
99-
request.target,
100-
envelope,
101-
limits,
102-
maxDepth,
103-
);
101+
const acquisition = createAcquisition(request.hint, request.target, envelope, limits);
104102
recordRecovery(
105103
host,
106104
depthHints,
@@ -216,7 +214,6 @@ function createAcquisition(
216214
target: SnapshotSourceRequest['target'],
217215
envelope: SnapshotBridgeEnvelope,
218216
limits: SnapshotSourceLimits,
219-
maxDepth: number,
220217
): IosSnapshotAcquisition {
221218
if (envelope.automationEnabled !== true) {
222219
throw snapshotSourceError('unsupported', 'automation-mode-unavailable');
@@ -243,14 +240,7 @@ function createAcquisition(
243240
const nodes = Object.freeze(
244241
decoded.nodes.map((node) => Object.freeze({ ...node, pid: target.pid })),
245242
);
246-
const residue = createAcquisitionResidue(
247-
hint,
248-
truncated,
249-
decoded,
250-
limits,
251-
maxDepth,
252-
nodes.length,
253-
);
243+
const residue = createAcquisitionResidue(hint, truncated, decoded.viewport);
254244
const lineage = Object.freeze({
255245
...(target.targetId ? { targetId: target.targetId } : {}),
256246
generation,
@@ -276,36 +266,16 @@ function createAcquisition(
276266
function createAcquisitionResidue(
277267
hint: CaptureHint,
278268
truncated: boolean,
279-
decoded: ReturnType<typeof decodeSnapshotBridgeTree>,
280-
limits: SnapshotSourceLimits,
281-
maxDepth: number,
282-
nodeCount: number,
269+
viewport: IosViewportEvidence,
283270
) {
284271
return Object.freeze([
285272
{ kind: 'unavailable-fact', fact: 'hittability' } as const,
286273
...(hint.interactiveOnly
287274
? ([{ kind: 'unavailable-fact', fact: 'interactive-query' }] as const)
288275
: []),
289-
...(truncated
290-
? [truncationResidue(decoded.maxTraversalDepth, nodeCount, limits, maxDepth)]
291-
: []),
292-
...(decoded.viewport.kind === 'missing'
293-
? ([{ kind: 'missing-viewport', reason: decoded.viewport.reason }] as const)
276+
...(truncated ? ([{ kind: 'truncated' }] as const) : []),
277+
...(viewport.kind === 'missing'
278+
? ([{ kind: 'missing-viewport', reason: viewport.reason }] as const)
294279
: []),
295280
]);
296281
}
297-
298-
function truncationResidue(
299-
maxTraversalDepth: number,
300-
nodeCount: number,
301-
limits: SnapshotSourceLimits,
302-
maxDepth: number,
303-
): { kind: 'truncated'; dimension: 'nodes' | 'depth' | 'payload'; limit?: number } {
304-
if (nodeCount >= limits.maxNodes) {
305-
return { kind: 'truncated', dimension: 'nodes', limit: limits.maxNodes };
306-
}
307-
if (maxTraversalDepth >= maxDepth) {
308-
return { kind: 'truncated', dimension: 'depth', limit: maxDepth };
309-
}
310-
return { kind: 'truncated', dimension: 'payload', limit: limits.maxResponseBytes };
311-
}

0 commit comments

Comments
 (0)