Skip to content

fix(daemon): enforce provider session artifact ownership - #2109

Merged
thymikee merged 5 commits into
claude/agent-device-2095-af369dfrom
fix/agent-device-2096
Aug 28, 2026
Merged

fix(daemon): enforce provider session artifact ownership#2109
thymikee merged 5 commits into
claude/agent-device-2095-af369dfrom
fix/agent-device-2096

Conversation

@thymikee

@thymikee thymikee commented Aug 27, 2026

Copy link
Copy Markdown
Member

Summary

Validation

  • Focused ownership, lifecycle, registry, provider, daemon, and provider-integration tests: 41/41 passed.
  • pnpm check:affected --run: 203 files and 1,292 tests passed; all runnable checks passed, including wire compatibility.
  • Red-first regressions were observed before the fix for daemon foreign and bare access, WebDriver bare-id access, lazy expiry cleanup, late provider responses, and the sequenced clock-boundary race.
  • Rebased onto the updated fix(daemon): fail closed when the auth hook is silent about tenant #2104 tip (e4d9a2fe5a) after that PR's stack advanced; exact-head CI is green (12 substantive checks, including Android/iOS/Linux/macOS smoke), with only the expected matrix skip.

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 2.48 MB 2.49 MB +2.3 kB
JS gzip 835.0 kB 835.8 kB +768 B
npm tarball 958.5 kB 959.3 kB +739 B
npm unpacked 3.32 MB 3.32 MB +2.3 kB

npm unpacked components

Component Base Current Diff
JS / dist source 2.64 MB 2.64 MB +2.3 kB
Apple runner source/project 581.1 kB 581.1 kB 0 B
macOS helper source 54.8 kB 54.8 kB 0 B
Android helper artifacts 0 B 0 B 0 B
Other package files 45.4 kB 45.4 kB 0 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 30.0 ms 30.1 ms +0.1 ms
CLI --help 83.2 ms 83.0 ms -0.2 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/perf-runtime-plan.js +630 B +154 B
dist/src/src2.js +209 B +46 B
dist/src/session2.js +9 B -1 B
dist/src/internal/daemon.js +9 B +1 B

Top changed packed files

Packed file Base Current Diff
dist/src/provider-session-ownership.js 0 B 5.4 kB +5.4 kB
dist/src/artifact-tracking.js 4.3 kB 0 B -4.3 kB
dist/src/perf-runtime-plan.js 63.6 kB 64.2 kB +630 B
dist/src/lease.js 3.7 kB 4.1 kB +408 B
dist/src/src2.js 47.3 kB 47.5 kB +209 B
dist/src/internal/daemon.js 107.9 kB 108.0 kB +9 B
dist/src/session2.js 216.0 kB 216.0 kB +9 B

@thymikee

Copy link
Copy Markdown
Member Author

P1 — expiry retention is measured from lazy cleanup, not from lease expiry. consumeExpiredLeases discovers an expired lease only when a later registry operation runs; unbindLease then calls markLeaseReleased, which sets retainedUntil = now + retentionMs. A first artifact request hours after lease.expiresAt therefore creates a fresh five-minute authorization window. The test masks this by resolving at expiresAt + 1, which starts the window immediately. Carry the actual transition time into ownership retention (explicit release uses current time; expiry uses lease.expiresAt), and plant a regression that jumps directly past expiresAt + retention before the first resolve and verifies refusal with no provider call. Also rerun/base-compare the currently red Android native-alert timeout before readiness.

@thymikee

Copy link
Copy Markdown
Member Author

Fixed in df3b681. Expiry cleanup now anchors provider-session retention at lease.expiresAt, and the regression jumps directly past expiresAt plus retention before the first resolve. Focused tests are 39/39 and pnpm check:affected --run passed all runnable checks. The Android native-alert rerun remains queued.

@thymikee

Copy link
Copy Markdown
Member Author

Re-reviewed df3b68183a: the normal lazy-cleanup expiry case is fixed and the direct regression is valid. One P1 remains. recordProviderSession() first cleans an expired lease, then records a provider session returned late and marks it released with default now(). A delayed allocation/release response can therefore create a fresh authorization window from response time instead of the true lease expiry. Preserve the terminal timestamp when recording an already-expired lease (while keeping manual release semantics distinct), and add planted-red coverage where provider allocation/release returns a session only after expiry+retention; no provider artifact dispatch may occur. Android/iOS smoke are still incomplete.

@thymikee

Copy link
Copy Markdown
Member Author

Fixed in 1e41afe. recordProviderSession now preserves lease.expiresAt when a late allocation or release response arrives after expiry; manual release still uses current time. Added planted-red coverage for both delayed paths with no provider dispatch. Focused tests are 41/41 and pnpm check:affected --run passes 203 files and 1,292 tests. The Android rerun passed on the prior head; current checks are running for this follow-up.

@thymikee

Copy link
Copy Markdown
Member Author

Re-reviewed 1e41afe29f: the late allocation/release fix still leaves a clock-boundary P1. recordProviderSession computes releasedAt with one now() call before cleanupExpiredLeases(), whose later now() can cross expiresAt. Cleanup then removes the lease, but releasedAt is still undefined and markLeaseReleased defaults to response-time now(), recreating the fresh retention window. The current late tests use a constant jumped clock and miss this boundary. Derive the expiry terminal time after cleanup (or make cleanup use the same captured timestamp) and plant a sequenced-clock regression crossing expiry between those calls; no provider dispatch may occur after expiry+retention. Manual release semantics must remain current-time. Exact-head CI is also incomplete.

@thymikee

Copy link
Copy Markdown
Member Author

Fixed in 6040534. recordProviderSession now derives the expiry terminal time after cleanup, so a clock crossing expiresAt between calls cannot reopen the retention window. Added a sequenced-clock red/green regression proving no provider dispatch after expiry plus retention. Focused 41/41 and pnpm check:affected --run (203 files, 1,292 tests) pass; exact-head CI is running after this push.

@thymikee

Copy link
Copy Markdown
Member Author

Code review is clean at 6040534afb2ab9dd839cc1a5722aed4599091a12: deriving terminal time after cleanup fixes the two-now() expiry-boundary race, and the sequenced-clock regression is non-vacuous. Manual-release retention remains anchored to current release time. This is ready for human review, but not merge-ready while exact-head Android and iOS smoke checks are still running.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Aug 27, 2026
@thymikee

Copy link
Copy Markdown
Member Author

Merge-ready at 6040534afb2ab9dd839cc1a5722aed4599091a12: all 12 substantive exact-head checks are green, including Android/iOS/Linux/macOS smoke, with only the expected matrix skip. Please replace the stale “CI … now running” line in Validation with the completed exact-head evidence.

@thymikee thymikee removed the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Aug 28, 2026
@thymikee

Copy link
Copy Markdown
Member Author

The reviewed head is unchanged, but the newly advanced main now conflicts with this branch. I removed ready-for-human; rebase and resolve the branch blocker before the next readiness review.

@thymikee
thymikee force-pushed the fix/agent-device-2096 branch from 6040534 to 15227f0 Compare August 28, 2026 08:52
@thymikee

Copy link
Copy Markdown
Member Author

Reviewed exact head 15227f0: code is clean. Daemon-side ownership resolution, zero-dispatch rejection cases, WebDriver binding checks, bounded retention, and expiry anchoring are covered; all exact-head substantive checks are green and size stays below escalation thresholds. Two process items remain: this PR is stacked on open prerequisite #2104, so it is not independently landable to main; and the body still says exact-head CI is running although it has completed green. Update that validation sentence. Readiness label will be handled by the coordinator after the prerequisite review.

@thymikee
thymikee force-pushed the fix/agent-device-2096 branch from 15227f0 to 6381863 Compare August 28, 2026 09:33
@thymikee

Copy link
Copy Markdown
Member Author

Rebased again onto #2104's new tip (423cd18232) after that PR's trust posture changed (fail-closed is now unconditional when a hook doesn't attest, regardless of client input). Clean rebase, no conflicts, same 9-file diff. Verified with a proper pnpm install: tsc, oxlint, and the focused ownership/lifecycle/registry/provider/wire-compat suites (60/60) pass. Watching CI now.

@thymikee
thymikee merged commit 7b8578e into main Aug 28, 2026
18 checks passed
@thymikee
thymikee deleted the fix/agent-device-2096 branch August 28, 2026 09:50
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-28 09:50 UTC

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.

daemon (remote): artifacts --provider-session forwards an unowned provider session id to the cloud provider with no tenant ownership check

1 participant