Skip to content

fix: bind capture adoption to session lifetimes - #3136

Closed
thymikee wants to merge 20 commits into
refactor/session-lifetime-entriesfrom
fix/session-capture-bindings
Closed

thymikee wants to merge 20 commits into
refactor/session-lifetime-entriesfrom
fix/session-capture-bindings

Conversation

@thymikee

@thymikee thymikee commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Summary

Capture adoption now receives a binding to one session lifetime. The binding owns admission, slot identity, artifact addressing and explicit field updates. Audio, perf, app-log and screen-recording callers capture their ref before asynchronous startup.

An unpublished record-only draft publishes at the existing adoption point. Shutdown can refuse publication while retaining truthful recovery evidence when cleanup is unconfirmed. Failed adoption rechecks authority after cleanup yields, so it cannot terminalize successor evidence.

flowchart LR
  Start[Start native capture] --> Adopt[Lifetime-bound adoption]
  Adopt --> Check{Admission and slot still valid?}
  Check -->|Yes| Publish[Publish owned resource]
  Check -->|No| Cleanup[Dispose captured handle]
  Cleanup --> Evidence[Retain unconfirmed recovery evidence if still authorized]
Loading

Depends on #3135. Ref #3116. Thirty-two files, 797 gross changed lines. Capture finishing migrates in the next dependency layer.

Validation

Tested d0f7cf8121: quick checks, Fallow and exact-head affected checks pass, including 355 related files / 2,357 tests. Focused draft/resource/log tests pass.

Removing admission or lifetime checks, reusing pre-await persistence authority, and coupling draft persistence to admission each fail the regression controls; restored code passes. Independent read-only review has no findings. CI-owned coverage/provider and live device evidence remain pending.

Review in cubic

@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.96 MB 4.96 MB +2.7 kB
Package (unpacked) 4.96 MB 4.96 MB +2.7 kB
Package (download) 1.49 MB 1.49 MB +1.0 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 28.0 ms 28.1 ms +0.0 ms
CLI --help 86.4 ms 86.7 ms +0.3 ms

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 32 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/daemon/__tests__/session-capture-binding.test.ts
Comment thread src/daemon/session-capture-binding.ts
Comment thread src/daemon/__tests__/perf-capture-session-resource.test.ts
Comment thread src/daemon/session-observability/internal/session-perf-runtime.ts
Comment thread src/daemon/__tests__/session-capture-binding.test.ts
@thymikee

thymikee commented Oct 3, 2026

Copy link
Copy Markdown
Member Author

The code in d0f7cf8 looks sound, but the PR is not ready: live device evidence is still missing, and the Coverage check fails because of this change.

Coverage fails in scripts/__tests__/eager-closure-budgets.test.ts. src/daemon/session-teardown.ts went from 46 to 47 evaluated modules. The new module is src/daemon/session-capture-binding.ts. It comes in through the static import at app-log-session-resource.ts:19, and teardown already imports that file for forceCleanupSessionAppLog. I did not run this test locally. I traced the cause from the CI import route and the diff. No conflicts.

The live run should reach three routes and show their output. (1) On a fresh session name with no prior open, whole-screen record start should publish the record-only session, so session list shows it, and record stop should return a playable artifact path. This is the draft-publication route. (2) On an opened session, record start/record stop and logs start/logs stop should succeed and leave no cleanup-pending resource record. (3) Where the device supports them, perf capture start/stop and audio capture start/status should work. This PR changes all four device-facing start paths, and the PR body says live evidence is still pending.

Could one binding constructor own the rule instead? The rule would be that a capture binding derives its slot only from the durable definition's sessionSlot. A method on SessionStore, for example sessionStore.bindCapture(ref, definition.sessionSlot), would sit beside the new assertAdmissionOpen/assertPublishable and write through sessionStore.update(ref, s => slot.replace(s, r)). The four per-kind adapters (audio, perf, screen recording, app log) already restate the sessionSlot.read and sessionSlot.replace that each durable definition declares. That would delete session-capture-binding.ts, the three per-kind binding files, bindSessionAppLog and the three new ownership-table entries. It would also fix the eager-closure budget without a dynamic import, because teardown imports session-store.ts only as a type and already evaluates app-log-session-resource.ts, so the call adds no module. The record-only draft can stay as a small variant of the same method. Nothing has to change first, since the definitions already declare sessionSlot.

Not blocking, and fine to take or leave: (a) Finishing is still keyed by address. clearLiveSlot and forceCleanupLiveDurableCapture in transitions.ts:193 call sessionStore.set(sessionName, ...) after awaiting the native finish, so a successor published at that address during the await would be overwritten. The rule for #3138 is that every finish caller (teardown, close, logs stop, record stop, audio restart, perf stop) holds a ref-bound binding and clears through binding.clear(expected). (b) No test reaches the draft binding's canPersist branch in screen-recording-session-binding.ts:26, where another request publishes the address while a record-only start is in flight. A test could publish the address after the draft is created, run adoptStartedScreenRecording, and assert session_address_occupied, a disposed handle and an unchanged winner record. (c) DurableCaptureSessionBinding.clear has no production caller here, so it could move to #3138, and draft! in record-runtime.ts:144 could go if both session shapes return one {binding, requireRef}.

I judged the tests by reading them and did not run the mutations the PR describes. I also could not confirm whether per-session request serialization makes the read() fallback window reachable in production. #3138 is not in this head, so I did not check what it fixes.

Before merge, please remove the new static edge from the teardown closure so Coverage passes, then attach the live record-only record start/record stop and logs start/logs stop output.

@thymikee
thymikee added this pull request to stack #3146 October 3, 2026 08:46
@thymikee
thymikee force-pushed the refactor/session-lifetime-entries branch from e180d9f to a760a6a Compare October 3, 2026 14:41
@thymikee
thymikee force-pushed the fix/session-capture-bindings branch 2 times, most recently from 712e7da to bef2ebb Compare October 3, 2026 16:41
@thymikee
thymikee force-pushed the refactor/session-lifetime-entries branch from a760a6a to 4011de9 Compare October 3, 2026 16:41
@thymikee

thymikee commented Oct 3, 2026

Copy link
Copy Markdown
Member Author

The two new commits fix the declaration emit and remove the static import that pulled session-capture-binding.ts into the session-teardown closure. At 696ea9f every check is still queued, so I can't yet say whether the eager-closure budget is met. The earlier Coverage failure (session-teardown 46 -> 47) was the target of that change, so the Coverage run will show it. I don't see any conflicts.

The live evidence requested earlier is still missing. This PR changes device-facing routes: record-only draft publication, record, logs, perf and audio-probe adoption, and teardown, starting in record-runtime.ts. The tests use session-binding fixtures, not the daemon route, so the real adoption paths on a device or simulator are unproven. Please post live run output on one device or simulator that shows: (1) on a fresh session name with no prior open, record start makes the session appear in session list, and record stop returns a playable artifact path; (2) on an opened session, record start/record stop and logs start/logs stop succeed, and no cleanup-pending resource record is left in the session dir; (3) where the device supports it, perf start/stop succeeds, and close with an active app log tears it down through the new lazy-import path with no app_log cleanup failure in the close response.

Before merge, I need that live output and a green Coverage run at 696ea9f.

@thymikee
thymikee force-pushed the refactor/session-lifetime-entries branch from 4011de9 to 4e4f87d Compare October 3, 2026 17:49
@thymikee
thymikee force-pushed the fix/session-capture-bindings branch from 696ea9f to 2652dd2 Compare October 3, 2026 17:49
…eclarations

Daemon session bindings inferred a binding type whose clear result was an
alias the durable-capture entry never exported, so declaration emit failed
with TS2883.
Binding app-log capture through the session capture binding pulled that module
into session-teardown's eager closure. Teardown now imports the app-log
resource only when a session has an app log to stop.
@thymikee
thymikee removed this pull request from stack #3146 October 3, 2026 19:38
@thymikee
thymikee force-pushed the fix/session-capture-bindings branch from 2652dd2 to cbf0a6e Compare October 3, 2026 19:39
@thymikee
thymikee force-pushed the refactor/session-lifetime-entries branch from 4e4f87d to 4ef85e9 Compare October 3, 2026 19:39
@thymikee
thymikee added this pull request to stack #3187 October 3, 2026 19:45
@thymikee
thymikee removed this pull request from stack #3187 October 3, 2026 20:58
@thymikee
thymikee force-pushed the refactor/session-lifetime-entries branch from 4ef85e9 to 5aa8134 Compare October 3, 2026 21:00
@thymikee

thymikee commented Oct 3, 2026

Copy link
Copy Markdown
Member Author

Consolidated into #3135 as part of reducing #3116 to seven PRs. The composition preserves the complete pre-consolidation source tree, including tests and later review corrections. This PR is superseded; its review discussion and native evidence remain available. Outstanding findings transfer to the owning keeper in the implementation record.

@thymikee thymikee closed this Oct 3, 2026
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