You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Clean snapshot ownership before an earned package extraction
Related to #1739 Wave 4 as behavior-neutral snapshot-domain prework. This issue does not migrate the snapshot command and does not authorize a new workspace package.
Decision
Clean up the ownership problems that are already demonstrated, then run the package/ownership checkpoint before #1739 Wave 4's snapshot unit is scoped.
Do not extract @agent-device/snapshot-semantics as part of this issue. The current proposal would turn a healthy, focused root zone into a broad namespace package whose facade mirrors unrelated callers. Packaging is justified when more than one implementation or a concrete packages/platform-* consumer needs the same pure operation—not by root fan-in alone.
After the two cleanup PRs land, the scheduled checkpoint must decide the owners of the root-only helpers Wave 4 needs. Its output is a required input to that unit:
no cohesive package boundary: record the contracts/kernel/platform ownership moves and close the extraction track; or
cohesive boundary with concrete Wave 4 callers: open a focused extraction issue for the smallest co-occurring operation set.
Evidence
The current src/snapshot zone has 12 production files, 1,786 lines, and no implementation file above 422 lines. Its size and topology are not themselves architecture debt.
The main deliverable is dependency reduction. The repository dependency graph currently reports 35 distinct external non-test consumers of src/snapshot. After PRs 1 and 2 relocate the non-cohesive quality, processing, tree, disclosure, Android-overlay, and label-signal responsibilities, the remaining cohesive cluster is expected to have 15 consumers: a reduction of 20 consumers, or about 57%. The checkpoint must recompute and attach these figures from the landed tree.
The relevant cross-boundary evidence is narrower:
src/platforms/android/snapshot.ts imports deriveMobileSnapshotHiddenContentHints from root snapshot code;
Android recovery code imports AndroidContentRecoveryReason from generic snapshot quality code;
src/platforms/apple/interactor.ts imports readSnapshotQualityVerdict from root snapshot code;
current packages/platform-* and provider packages import no root snapshot implementation today;
The proposed package facade grouped presentation, visibility, occlusion, line rendering, label deduplication, diffing, readable text, and scroll-indicator interpretation. Those operations rarely co-occur at current callers and do not answer one cohesive question. Moving them together would improve physical placement while weakening the module interface.
ADR 0019 requires shared logic to be earned by more than one implementation. The current package graph does not yet contain that consumer, but #1739 already schedules the forcing event. This issue must produce the ownership decision before Wave 4 starts rather than deferring it into the command-unit PR.
Scope
PR 1: de-Android snapshot quality
This cleanup is independently valuable and should land even if no package is ever extracted.
Move ANDROID_CONTENT_RECOVERY_REASONS, its derived AndroidContentRecoveryReason, the reason guard, and the typed unreadable-content classifier to @agent-device/contracts/platform as one source of truth.
Make src/platforms/android/snapshot-content-recovery.ts derive from that contract. Delete the duplicate tuple/type from generic quality code.
Update wait/replay consumers to import the explicitly Android-owned classifier from contracts. Do not create a commands-to-src/platforms/android value edge; R3 must remain green.
Split generic quality by question:
src/snapshot-quality/verdict.ts owns parsing and sparse classification;
This split repoints src/platforms/apple/interactor.ts to src/snapshot-quality/verdict.ts; it does not resolve that Apple snapshot-path root import. The pre-Wave 4 checkpoint owns its final destination.
Add the snapshot-quality rank while the existing rank-1 snapshot zone remains.
PR 2: simplify root ownership without packaging it
Relocate or delete the proven leftovers while keeping cohesive snapshot algorithms in the existing root zone.
Delete displayNodeLabel after proving its simple label/value/identifier behavior is covered by the existing extractNodeText operation and converting its callers.
Inline the 12-line Android input-method overlay delegation at its two owners and delete android-input-method-overlays.ts.
Move Android system-surface disclosure wording to src/core/android-system-surface-disclosure.ts, preserving R3.
Split snapshot-processing.ts only where ownership is clear:
lookup/ref-label resolution to src/core/snapshot-node-lookup.ts;
pruning and tree normalization to src/core/snapshot-tree-ingestion.ts;
hittable-ancestor resolution to interaction targeting;
generic ancestor traversal to the existing contracts tree walk.
Fold the single-use repeated-label signal into repeated-navigation detection and delete its leaf module.
Split finite rect normalization/approximate equality into kernel rect utilities only if required by the resulting owners; keep touch-center/interior policy in interaction/core.
Keep these modules physically in root during PRs 1 and 2:
mobile presentation and hidden-content analysis;
visibility response mapping;
generic occlusion and its blocked-target enforcement operation;
line/role/label formatting;
label deduplication;
structural diffing and comparable-line counting;
readable-text and scroll-indicator interpretation.
The checkpoint must decide the durable owners of deriveMobileSnapshotHiddenContentHints, readable-text helpers, and scroll-indicator helpers before Wave 4's snapshot unit is scoped. Keeping them in root during cleanup is not a decision to keep them there through the platform cutover.
Do not move a utility merely because a hypothetical package would otherwise violate R11.
Capture behavior that must not change
Preserve the exact orchestration order:
rawNodes
-> pruneGroupNodes unless snapshotRaw
-> normalizeSnapshotTree
-> scopeSnapshotNodes when snapshotScope && backend !== macos-helper
-> presentIosInteractiveSnapshot when selected by its backend/flag policy
-> annotateCoveredSnapshotNodes unless snapshotRaw
additional overlay classifier selected only when backend === android
-> attachRefs
Do not introduce prepareSnapshot, transformation hooks, or a convenience facade that can obscure or reorder these branches.
Characterization coverage must exercise raw and non-raw paths, scoped and unscoped paths, macos-helper scope behavior, conditional iOS presentation, conditional Android overlay classification, and final ref attachment.
Pre-Wave 4 ownership checkpoint
Run the checkpoint after PRs 1 and 2 land and before #1739 Wave 4's snapshot unit is scoped. Its recorded output is a prerequisite for that unit, not an open-ended remeasurement.
At minimum, decide the owners of:
deriveMobileSnapshotHiddenContentHints, needed by the current Android path and relevant to the coming platform boundary;
readable-text selection used across snapshot and interaction presentation;
scroll-indicator interpretation imported by src/daemon/snapshot-presentation/ios/scroll.ts and required by the scheduled platform-apple relocation;
readSnapshotQualityVerdict in post-PR-1 src/snapshot-quality/verdict.ts, consumed by the Apple snapshot capture path in src/platforms/apple/interactor.ts.
A follow-up package proposal must show all of the following:
The scheduled platform-apple consumer—or another concrete second implementation—needs the same named pure operations. Wave 4 is a forcing event, but it does not by itself justify unrelated exports.
The candidate answers one domain question and exposes a small exact interface. An expected range is 3–5 exports; a larger surface needs caller co-occurrence evidence.
Callers consume the operations together, or the interface demonstrably hides substantial policy rather than reproducing a namespace.
The package depends only on justified lower-ranked packages and creates no root back-import.
No helper becomes public solely because package isolation would otherwise forbid importing its current owner.
The proposal records the post-cleanup import edges, the exact Wave 4 callers, and the contracts/kernel/platform-placement alternative.
Only after that decision should a focused follow-up amend ADR 0019/R13, add package-specifier enforcement support, change interaction-guarantee via entries, touch mutation package ownership, or pay for a full mutation-baseline reset.
The future package should be named for the single question it answers. snapshot-semantics is intentionally not reserved: it is too broad for the evidence currently available.
Out of scope
creating any snapshot workspace package in PRs 1 or 2;
changing ADR 0019 or R13 before the checkpoint selects a package;
moving text-surface, scroll-indicator, visibility, lines, diff, deduplication, or occlusion solely for package isolation;
physically relocating src/daemon/snapshot-presentation/** in this issue—the ownership decision for its root-only dependencies is in scope and must precede that Wave 4 move;
package-specifier support in interaction-guarantee or depgraph tooling before a package is selected;
mutation configuration/baseline changes caused only by a hypothetical package;
command behavior, CLI help, or skills.
Acceptance evidence
PR 1
One Android recovery reason definition exists in @agent-device/contracts/platform.
Producer, wait, and replay classification cannot diverge.
A focused planted violation proves R3 still blocks commands from statically value-importing src/platforms/android.
Quality tests mirror the new verdict.ts and warnings.ts topology.
The new snapshot-quality DAG classification is covered by its model test.
PR 2
Each deletion or relocation has characterization coverage at the owning interface.
displayNodeLabel equivalence is proven against current caller fixtures before deletion.
Every capture-pipeline branch above preserves output and ordering.
No compatibility barrel or forwarding module remains.
Fallow baseline keys are moved exactly for renamed files; unrelated baselines are not regenerated.
The post-cleanup dependency graph lists every remaining root-snapshot import from src/platforms and packages/platform-* explicitly and verifies the expected reduction from 35 external consumers to approximately 15.
pnpm check passes because this is a broad structural refactor.
pnpm check:affected --run passes immediately before push.
Records one owner decision for scroll-indicator, readable-text, hidden-content-hint, and snapshot-quality-verdict semantics.
Names the exact Wave 4 callers and proves platform-apple will retain no root imports.
Either closes the package track with explicit contracts/kernel/platform destinations or opens a narrowly named package issue with its minimum interface and required ADR/R13/tooling work.
Review checkpoint
Attach the post-cleanup dependency-graph evidence to this issue. The result must be recorded before Wave 4 begins:
no cohesive qualifying package cluster: document the non-package ownership moves required by Wave 4 and close the extraction track;
cohesive qualifying cluster: open a new, narrowly named package issue with the minimum interface and its concrete platform-apple/other callers.
Docs impact: none for command behavior. Architecture documentation changes only if the checkpoint accepts a later package extraction.
Clean snapshot ownership before an earned package extraction
Related to #1739 Wave 4 as behavior-neutral snapshot-domain prework. This issue does not migrate the snapshot command and does not authorize a new workspace package.
Decision
Clean up the ownership problems that are already demonstrated, then run the package/ownership checkpoint before #1739 Wave 4's snapshot unit is scoped.
Do not extract
@agent-device/snapshot-semanticsas part of this issue. The current proposal would turn a healthy, focused root zone into a broad namespace package whose facade mirrors unrelated callers. Packaging is justified when more than one implementation or a concretepackages/platform-*consumer needs the same pure operation—not by root fan-in alone.After the two cleanup PRs land, the scheduled checkpoint must decide the owners of the root-only helpers Wave 4 needs. Its output is a required input to that unit:
Evidence
The current
src/snapshotzone has 12 production files, 1,786 lines, and no implementation file above 422 lines. Its size and topology are not themselves architecture debt.The main deliverable is dependency reduction. The repository dependency graph currently reports 35 distinct external non-test consumers of
src/snapshot. After PRs 1 and 2 relocate the non-cohesive quality, processing, tree, disclosure, Android-overlay, and label-signal responsibilities, the remaining cohesive cluster is expected to have 15 consumers: a reduction of 20 consumers, or about 57%. The checkpoint must recompute and attach these figures from the landed tree.The relevant cross-boundary evidence is narrower:
src/platforms/android/snapshot.tsimportsderiveMobileSnapshotHiddenContentHintsfrom root snapshot code;AndroidContentRecoveryReasonfrom generic snapshot quality code;src/platforms/apple/interactor.tsimportsreadSnapshotQualityVerdictfrom root snapshot code;packages/platform-*and provider packages import no root snapshot implementation today;src/daemon/snapshot-presentation/ios/to move behindplatform-appleand explicitly requires ownership decisions for its root-only overlay, scroll-indicator, and tree helpers first;The proposed package facade grouped presentation, visibility, occlusion, line rendering, label deduplication, diffing, readable text, and scroll-indicator interpretation. Those operations rarely co-occur at current callers and do not answer one cohesive question. Moving them together would improve physical placement while weakening the module interface.
ADR 0019 requires shared logic to be earned by more than one implementation. The current package graph does not yet contain that consumer, but #1739 already schedules the forcing event. This issue must produce the ownership decision before Wave 4 starts rather than deferring it into the command-unit PR.
Scope
PR 1: de-Android snapshot quality
This cleanup is independently valuable and should land even if no package is ever extracted.
ANDROID_CONTENT_RECOVERY_REASONS, its derivedAndroidContentRecoveryReason, the reason guard, and the typed unreadable-content classifier to@agent-device/contracts/platformas one source of truth.src/platforms/android/snapshot-content-recovery.tsderive from that contract. Delete the duplicate tuple/type from generic quality code.src/platforms/androidvalue edge; R3 must remain green.src/snapshot-quality/verdict.tsowns parsing and sparse classification;src/snapshot-quality/warnings.tsowns warning rendering.src/platforms/apple/interactor.tstosrc/snapshot-quality/verdict.ts; it does not resolve that Apple snapshot-path root import. The pre-Wave 4 checkpoint owns its final destination.snapshot-qualityrank while the existing rank-1snapshotzone remains.PR 2: simplify root ownership without packaging it
Relocate or delete the proven leftovers while keeping cohesive snapshot algorithms in the existing root zone.
displayNodeLabelafter proving its simple label/value/identifier behavior is covered by the existingextractNodeTextoperation and converting its callers.android-input-method-overlays.ts.src/core/android-system-surface-disclosure.ts, preserving R3.snapshot-processing.tsonly where ownership is clear:src/core/snapshot-node-lookup.ts;src/core/snapshot-tree-ingestion.ts;Keep these modules physically in root during PRs 1 and 2:
The checkpoint must decide the durable owners of
deriveMobileSnapshotHiddenContentHints, readable-text helpers, and scroll-indicator helpers before Wave 4's snapshot unit is scoped. Keeping them in root during cleanup is not a decision to keep them there through the platform cutover.Do not move a utility merely because a hypothetical package would otherwise violate R11.
Capture behavior that must not change
Preserve the exact orchestration order:
Do not introduce
prepareSnapshot, transformation hooks, or a convenience facade that can obscure or reorder these branches.Characterization coverage must exercise raw and non-raw paths, scoped and unscoped paths,
macos-helperscope behavior, conditional iOS presentation, conditional Android overlay classification, and final ref attachment.Pre-Wave 4 ownership checkpoint
Run the checkpoint after PRs 1 and 2 land and before #1739 Wave 4's snapshot unit is scoped. Its recorded output is a prerequisite for that unit, not an open-ended remeasurement.
At minimum, decide the owners of:
deriveMobileSnapshotHiddenContentHints, needed by the current Android path and relevant to the coming platform boundary;src/daemon/snapshot-presentation/ios/scroll.tsand required by the scheduledplatform-applerelocation;readSnapshotQualityVerdictin post-PR-1src/snapshot-quality/verdict.ts, consumed by the Apple snapshot capture path insrc/platforms/apple/interactor.ts.A follow-up package proposal must show all of the following:
platform-appleconsumer—or another concrete second implementation—needs the same named pure operations. Wave 4 is a forcing event, but it does not by itself justify unrelated exports.Only after that decision should a focused follow-up amend ADR 0019/R13, add package-specifier enforcement support, change interaction-guarantee
viaentries, touch mutation package ownership, or pay for a full mutation-baseline reset.The future package should be named for the single question it answers.
snapshot-semanticsis intentionally not reserved: it is too broad for the evidence currently available.Out of scope
src/daemon/snapshot-presentation/**in this issue—the ownership decision for its root-only dependencies is in scope and must precede that Wave 4 move;Acceptance evidence
PR 1
@agent-device/contracts/platform.src/platforms/android.verdict.tsandwarnings.tstopology.snapshot-qualityDAG classification is covered by its model test.PR 2
displayNodeLabelequivalence is proven against current caller fixtures before deletion.src/platformsandpackages/platform-*explicitly and verifies the expected reduction from 35 external consumers to approximately 15.pnpm checkpasses because this is a broad structural refactor.pnpm check:affected --runpasses immediately before push.Scheduled checkpoint
platform-applewill retain no root imports.Review checkpoint
Attach the post-cleanup dependency-graph evidence to this issue. The result must be recorded before Wave 4 begins:
platform-apple/other callers.Docs impact: none for command behavior. Architecture documentation changes only if the checkpoint accepts a later package extraction.