Skip to content

refactor(snapshot): type the acquisition producer beside the platform channel - #2000

Merged
thymikee merged 2 commits into
mainfrom
claude/snapshot-producer-provenance
Aug 24, 2026
Merged

thymikee merged 2 commits into
mainfrom
claude/snapshot-producer-provenance

Conversation

@thymikee

Copy link
Copy Markdown
Member

Summary

Give every snapshot a typed producer — the acquisition component that made the raw tree — as a third identity axis beside the platform channel (SnapshotBackend) and the in-plan capture strategy (SnapshotCaptureBackend).

Today three producers with different guarantees share the backend: 'xctest' stamp: the local Apple runner (whose output went through the runner's presentation — clip fold, effective geometry, scope), Appium page-source XML (provider-webdriver), and limrun element trees (provider-limrun) — the latter two reach the wire with none of those guarantees. The 'android' channel conflates the local uiautomator path with Appium-sourced trees the same way. Daemon logic keyed on the channel cannot tell them apart.

This is the types-before-semantics step for the shared-presentation track (#1983): no consumer changes behavior here. The field is what the host-side presenter wiring and the double-presentation removal will key on, and what a future XCTest-free simulator producer will register as.

  • SnapshotProducer union + axis doc in @agent-device/kernel/snapshot; SnapshotState.producer (optional, daemon-side only — the value does not cross the CLI wire, same as backend)
  • SnapshotResult.producer is required, so the compiler enumerates every producer: apple-runner, android-uiautomator, appium-source, limrun-ios-tree, macos-helper, linux-atspi, agent-browser, harmonyos-uitest
  • buildSnapshotState carries the producer into SnapshotState; the capture-handler SnapshotData seam passes it through
  • CONTEXT.md gains the Snapshot producer vocabulary entry

Validation

  • pnpm check:affected --run

@github-actions

github-actions Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
JS raw 2.40 MB 2.41 MB +733 B
JS gzip 806.0 kB 805.6 kB -430 B
npm tarball 930.2 kB 930.6 kB +365 B
npm unpacked 3.23 MB 3.23 MB +2.7 kB

npm unpacked components

Component Base Current Diff
JS / dist source 2.56 MB 2.56 MB +2.7 kB
Apple runner source/project 570.6 kB 570.6 kB 0 B
macOS helper source 54.5 kB 54.5 kB 0 B
Android helper artifacts 0 B 0 B 0 B
Other package files 44.7 kB 44.7 kB 0 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 22.2 ms 21.7 ms -0.5 ms
CLI --help 61.6 ms 62.9 ms +1.3 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/session2.js 0 B -181 B
dist/src/registry.js -5 B -121 B
dist/src/runtime.js 0 B -82 B
dist/src/sdk-batch-runner.js 0 B -55 B
dist/src/runner-client.js 0 B -27 B

Top changed packed files

Packed file Base Current Diff
dist/src/sdk-contracts.d.ts 16.3 kB 18.1 kB +1.8 kB
dist/src/sdk-contracts.js 10.5 kB 11.0 kB +452 B
dist/src/sdk-selectors.d.ts 29.4 kB 29.6 kB +196 B
dist/src/src2.js 48.7 kB 48.8 kB +95 B
dist/src/snapshot-desktop-surface.js 1.7 kB 1.8 kB +52 B
dist/src/snapshot.js 29.5 kB 29.5 kB +31 B
dist/src/harmonyos.js 6.4 kB 6.5 kB +28 B
dist/src/src.js 19.0 kB 19.0 kB +27 B
dist/src/web.js 1.2 kB 1.2 kB +25 B
dist/src/interactor.js 15.2 kB 15.2 kB +24 B

@thymikee
thymikee force-pushed the claude/snapshot-producer-provenance branch 2 times, most recently from 31fb491 to 50b7fd9 Compare August 24, 2026 13:53
@thymikee

Copy link
Copy Markdown
Member Author

Not ready at 31fb491c6. The typed SnapshotProducer seam is appropriately deep and the production propagation is sound, but two required gates are deterministically red. Changed-line coverage is 2/4 (50%): add direct producer assertions for packages/provider-limrun/src/ios.ts:260 and src/core/interactors/harmonyos.ts:48. Fallow also reports three above-threshold functions, including createAppleInteractor; resolve that health debt at the owning module rather than bypassing the gate. All other checks, including platform smokes, pass; no additional live-device evidence is required for this type-only slice once these gates are green.

@thymikee

Copy link
Copy Markdown
Member Author

Review of exact head 50b7fd95f39b6147bc87f80a3b7370c71d56ec44:

The prior coverage and Fallow blockers are resolved cleanly: Limrun iOS and HarmonyOS now assert their real producers, and coverage/Fallow are green without suppressions.

One remaining P2 contract gap blocks readiness: SnapshotResult makes backend and producer independent unions, so impossible provenance pairs such as { backend: "android", producer: "apple-runner" } type-check. This seam is being added specifically so future presentation logic can trust producer guarantees. Encode the valid channel-producer matrix as a discriminated provenance type, carry that coupled type through snapshot state, and add a negative type test. That makes the invariant structural instead of relying on each constructor to pair two independent strings correctly.

@thymikee

Copy link
Copy Markdown
Member Author

Re-reviewed 50b7fd95f39b6147bc87f80a3b7370c71d56ec44: CI is now fully green, including device smoke, but the code blocker remains. backend and producer are still independent unions, so invalid provenance pairs type-check. Please carry one kernel-owned discriminated provenance value atomically through SnapshotResult and state, with a negative type-level regression proving invalid pairs cannot compile. Not ready for human review yet.

thymikee added a commit that referenced this pull request Aug 24, 2026
Review follow-up on #2000: backend and producer were independent unions,
so cross-channel pairs type-checked. SnapshotProvenance now owns the
legal channel<->producer pairs; SnapshotBackend is its projection,
SnapshotResult embeds the strict pair, SnapshotState embeds the
optional-producer variant, and buildSnapshotState carries the pair
through a narrowing helper so the fields never decorrelate. Negative
type-level regression pins that invalid pairs cannot compile.
@thymikee

Copy link
Copy Markdown
Member Author

Re-reviewed 90cc71a4b29147a1b0e44399ad955ea85b229532: the kernel-owned discriminated provenance union and negative type-level proof fix the prior code blocker. This head is still not ready: CONTEXT.md is 12,073 bytes, 73 over its enforced budget, and the new glossary text includes implementation/architecture detail that belongs at the declaration/ADR seam. Also, Typecheck & Package is genuinely red at src/daemon/__tests__/test-device-runtime-gateway.ts:96 (the unavailableDeviceRuntimeGateway facts/binding no longer satisfy DeviceRuntimeGateway<PlatformRuntimeOperations>); Repo Guards repeats the same type error. Trim the glossary entry and repair the typed fixture boundary, then rerun the exact head.

thymikee added a commit that referenced this pull request Aug 24, 2026
Review follow-up on #2000: backend and producer were independent unions,
so cross-channel pairs type-checked. SnapshotProvenance now owns the
legal channel<->producer pairs; SnapshotBackend is its projection,
SnapshotResult embeds the strict pair, SnapshotState embeds the
optional-producer variant, and buildSnapshotState carries the pair
through a narrowing helper so the fields never decorrelate. Negative
type-level regression pins that invalid pairs cannot compile.
@thymikee
thymikee force-pushed the claude/snapshot-producer-provenance branch from 90cc71a to c588c7b Compare August 24, 2026 16:06
@thymikee

Copy link
Copy Markdown
Member Author

Re-reviewed c588c7b27308f8a7b1c9d960b8c771db9493df90: the context budget is fixed and Agent Guidance is green. The blocking type error remains unchanged—Typecheck & Package is red at src/daemon/__tests__/test-device-runtime-gateway.ts:96 because the snapshot fixture still returns an XCTest backend without the now-required producer, so the gateway no longer satisfies DeviceRuntimeGateway<PlatformRuntimeOperations>. Repair that typed fixture route and rerun the exact head. Small cleanup while there: keep the glossary definition path-free; the kernel table/path belongs at its declaration or ADR, not CONTEXT.md.

… channel

Three producers with different guarantees share the backend: 'xctest'
stamp (Apple runner, Appium page-source, limrun element trees), and the
'android' channel conflates the local uiautomator path with Appium
trees the same way. Add SnapshotProducer as a required field on
SnapshotResult so the compiler enumerates every producer, and carry it
into SnapshotState. Types-before-semantics step for #1983; no consumer
behavior changes.
@thymikee
thymikee force-pushed the claude/snapshot-producer-provenance branch from c588c7b to 83fd0fe Compare August 24, 2026 16:25
thymikee added a commit that referenced this pull request Aug 24, 2026
Review follow-up on #2000: backend and producer were independent unions,
so cross-channel pairs type-checked. SnapshotProvenance now owns the
legal channel<->producer pairs; SnapshotBackend is its projection,
SnapshotResult embeds the strict pair, SnapshotState embeds the
optional-producer variant, and buildSnapshotState carries the pair
through a narrowing helper so the fields never decorrelate. Negative
type-level regression pins that invalid pairs cannot compile.
@thymikee

Copy link
Copy Markdown
Member Author

Implementation is sound and the prior typed-fixture failure is fixed; completed checks are green. One P2 remains: the new CONTEXT.md Snapshot producer entry embeds declaration-path and architecture details (SnapshotProvenance, @agent-device/kernel/snapshot, atomic carrier rules). This repository keeps CONTEXT.md glossary-only. Trim it to the plain domain meaning and leave declaration/architecture ownership at the typed seam or ADR. iOS smoke is still pending.

Review follow-up on #2000: backend and producer were independent unions,
so cross-channel pairs type-checked. SnapshotProvenance now owns the
legal channel<->producer pairs; SnapshotBackend is its projection,
SnapshotResult embeds the strict pair, SnapshotState embeds the
optional-producer variant, and buildSnapshotState carries the pair
through a narrowing helper so the fields never decorrelate. Negative
type-level regression pins that invalid pairs cannot compile.
@thymikee

Copy link
Copy Markdown
Member Author

Re-review clean at 30bd8096: the glossary entry now stays domain-only, and the correlated snapshot provenance typing/fixtures are sound. Coverage (2) failed only in the pre-existing 1ms wait timeout decoration assertion; this PR changes that fixture solely by adding the required agent-browser producer and changes no wait behavior, so this is a timing flake rather than a code finding. Code is ready, but CI needs a green retry before merge.

@thymikee
thymikee merged commit 054dcd4 into main Aug 24, 2026
27 of 28 checks passed
@thymikee
thymikee deleted the claude/snapshot-producer-provenance branch August 24, 2026 17:11
@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-24 17:11 UTC

thymikee added a commit that referenced this pull request Aug 24, 2026
…ad (#2008)

* test(daemon): drive the timed-out wait decoration test on a fake clock

The 1ms wait timeout raced the real clock: under CI load the first capture
could take longer than 1ms to resolve, so runWithinWaitDeadline classified
the poll capture-stalled/capture-truncated instead of a completed
target-absent poll, and maybeWaitTimeoutSurfaceResponse skipped the
"Current surface" decoration outright — causing the intermittent CI Coverage
failure on PR #2000 (run 32752619090, head 30bd809).

Reproduced the exact failure locally by pegging all CPUs during the test
run, then fixed it the same way webdriver-interactor.test.ts's runFill
does: vi.useFakeTimers() plus advanceTimersByTimeAsync so virtual time only
moves when the test says so, removing the race entirely. Verified 10/10
passes under the same simulated load that reliably failed the old test.

* test(daemon): simplify the fake-clock timed-out wait test per review

Drop the justification comment and the settled-result wrapper: the
capture path in this scenario is caught end-to-end by toDaemonResponse,
so runWait cannot reject here, and the existing test name/assertions
already state the invariant.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant