Skip to content

refactor: bind daemon captures to session lifetimes - #3141

Closed
thymikee wants to merge 30 commits into
refactor/session-journal-lifetimesfrom
refactor/session-snapshot-lifetimes
Closed

thymikee wants to merge 30 commits into
refactor/session-journal-lifetimesfrom
refactor/session-snapshot-lifetimes

Conversation

@thymikee

@thymikee thymikee commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Summary

Snapshots, selectors and interaction captures now retain the session lifetime they were admitted for. A rebuild remains writable; retirement or daemon shutdown refuses late publication. Settle ref issuance resolves that same lifetime before activating the frame, so returned refs belong to the current snapshot.

The runtime projection owns the reference and refreshes records within its lifetime. Snapshot transitions stay in their owning module. Removes redundant store resets and the interaction capture's reverse name lookup. 43 files; 925 gross lines. Part of #3116, stacked on #3140.

flowchart LR
  C[Capture a session ref] --> R[Resolve its lifetime]
  R -->|current| U[Update observation and refs]
  R -->|retired| E[Refuse publication]
  D[Sessionless capture] --> A[Check admission and vacancy]
  A --> P[Publish a new lifetime]
Loading

Validation

Head 1c3da4c2c6: pnpm check:affected --base refactor/session-journal-lifetimes --run passed, including 1,613 related tests; parent-scoped Fallow passed. Seven planted regressions failed their intended assertions and the restored implementation passed.

Controls cover a held real touch-settle continuation, scoped address reuse, intervening rebuilds, and late capture completion after actual request cancellation and bounded daemon-session teardown.

Independent read-only review found no remaining actionable findings. CI and live device validation remain pending on this head.

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 +731 B
Package (unpacked) 4.96 MB 4.96 MB +731 B
Package (download) 1.49 MB 1.49 MB +297 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 28.1 ms 28.2 ms +0.1 ms
CLI --help 82.6 ms 85.2 ms +2.6 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 43 files

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

Re-trigger cubic

Comment thread src/daemon/selector-runtime.ts
Comment thread src/daemon/wait-runtime.ts
Comment thread src/daemon/snapshot-command-runtime.ts
Comment thread src/daemon/session-snapshot.ts
Comment thread src/daemon/selector-capture-runtime.ts
Comment thread src/daemon/interaction/internal/interaction-ios-tap-outcome.ts
Comment thread src/daemon/generic-settle.ts
Comment thread src/daemon/selector-capture-runtime.ts
Comment thread src/daemon/__tests__/session-snapshot.test.ts
@thymikee

thymikee commented Oct 3, 2026

Copy link
Copy Markdown
Member Author

Thanks for the refactor. At 1c3da4c, dispatchRuntimeInteraction likely has a defect: after a same-lifetime rebuild, press and fill write to a stale session object.

dispatchRuntimeInteraction resolves session = requireCurrent(ref) once at entry. The old capture reset put that same object back as the current entry. That reset is gone, so if a rebuild lands during the capture (store.update from lease-lifecycle, app-log, perf or recording), the capture publishes into the new record. Every later write still goes to the entry-time object, which no entry holds. This hits finalizeTouchInteraction at :108 (the deferred outcome marks, so Android ref-freshness and post-gesture stabilization are skipped), and recordAction, which misses its lookup and logs to the 'default' log instead of the scoped one. It also hits the ambiguity candidate refs and snapshotGeneration at :126-127, so the refs returned for AMBIGUOUS_MATCH are not valid in the current frame. The same pattern shows up in buildRuntimeIosCorroboratedResponse at :160, the expireRefFrame(session) call after the capture await in react-native.ts:163, and the one after an await in interaction.ts:156. I could not measure how often a rebuild overlaps an interaction, so I rate this as likely, not confirmed. The held touch-settle test builds this exact state but only asserts the settle refs.

The rule: after any await, an interaction route reads and writes session state only through sessionStore.requireCurrent(ref), never through a SessionState captured earlier. The sites to cover are every write after the capture await in the touch, react-native, type-text and iOS corroboration paths. Please make finalizeTouchInteraction, the ambiguity publisher and buildRuntimeIosCorroboratedResponse take the ref and resolve the current record at the write, and give SessionStore.recordAction a SessionRef. Then extend the held touch-settle test to assert the deferred mark and the event-log address on the current record.

This change also touches snapshot publication, which sits behind every device snapshot, selector read, press/fill and --settle. Smoke Tests have now passed on 1c3da4c, so all checks are green and I do not ask for a separate device run. I did not run tests locally, and I did not read recordIfSession at this head, so I have not verified the open threads at selector-runtime.ts:90 and wait-runtime.ts:160. Those threads still stand if they apply.

The router already resolves the session under the locked scope (request-router.ts:496). Could it resolve the SessionRef there and pass it as a required field on InteractionRouteInput and the generic dispatch params? That would remove bindInteractionSession, the 'sessionRef' in params probe, every sessionRef! assertion and the second lookup in request-generic-dispatch. With finalizeTouchInteraction and SessionStore.recordAction taking a ref, the last resolveStoredSessionName consumer on this route would go too. For that, the router's locked scope must own the SessionRef, and recordAction must accept one.

Not blocking, and you can take or leave it: bindInteractionSession re-looks up the ref at about 8 entry points, request-generic-dispatch.ts:78 looks it up separately from the router's earlier get so session and sessionRef can name different lifetimes, and the sessionRef rebinding at interaction.ts:91 is redundant, all of which the required-field change above would fix.

Before merge, the post-capture interaction writes need to resolve the current record through the SessionRef. There are no conflicts.

@thymikee
thymikee added this pull request to stack #3146 October 3, 2026 08:50
@thymikee
thymikee force-pushed the refactor/session-journal-lifetimes branch from a3bf16c to 039fc5c Compare October 3, 2026 14:41
@thymikee
thymikee force-pushed the refactor/session-snapshot-lifetimes branch from 1c3da4c to ede3e35 Compare October 3, 2026 14:41
@thymikee
thymikee force-pushed the refactor/session-journal-lifetimes branch from 039fc5c to d42ec00 Compare October 3, 2026 15:16
@thymikee
thymikee force-pushed the refactor/session-snapshot-lifetimes branch from ede3e35 to 1790d36 Compare October 3, 2026 15:16
@thymikee
thymikee force-pushed the refactor/session-journal-lifetimes branch from d42ec00 to fb3df65 Compare October 3, 2026 16:41
@thymikee
thymikee force-pushed the refactor/session-snapshot-lifetimes branch 2 times, most recently from 393dcb8 to 672583f Compare October 3, 2026 16:56
@thymikee
thymikee force-pushed the refactor/session-journal-lifetimes branch 2 times, most recently from 3ea090a to ec8a4f8 Compare October 3, 2026 17:49
@thymikee
thymikee force-pushed the refactor/session-snapshot-lifetimes branch from 672583f to 0cc39c1 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.
Session teardown imported three per-capture binding modules plus their shared
binding, which grew its eager closure past the merge-base. The audio, perf and
screen-recording bindings now live beside bindSessionCapture.
@thymikee
thymikee removed this pull request from stack #3146 October 3, 2026 19:38
@thymikee
thymikee force-pushed the refactor/session-snapshot-lifetimes branch from 0cc39c1 to e4ca3d1 Compare October 3, 2026 19:39
@thymikee
thymikee force-pushed the refactor/session-journal-lifetimes branch from ec8a4f8 to 7e96b83 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:59
@thymikee
thymikee force-pushed the refactor/session-journal-lifetimes branch from 7e96b83 to 130fe6e Compare October 3, 2026 21:00
@thymikee

thymikee commented Oct 3, 2026

Copy link
Copy Markdown
Member Author

Consolidated into #3140 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