Skip to content

fix(daemon): retain cleanup locks and preserve healed snapshots - #3760

Open
thymikee wants to merge 11 commits into
mainfrom
fix/adr-drift-lock-and-heal
Open

thymikee wants to merge 11 commits into
mainfrom
fix/adr-drift-lock-and-heal

Conversation

@thymikee

@thymikee thymikee commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Keep session/device execution locks through binding and claim cleanup on every outcome (ADR 0019).
  • Retain authored snapshots and explicit repair-time --record corrections in healed scripts (ADR 0012). Exclude diagnostic and foreground composite captures.
  • CLI/MCP ingress now distinguishes per-call intent from project record: true defaults; SDK per-call recording stays explicit.
  • Both fixes close the same ADR-conformance audit (Restructure ADRs into short decision records #3744). Twenty-three files; scope includes foreground composition and recording ingress. Binding disposal remains unbounded; existing disposers acquire no execution locks.

Validation

  • Tested SHA: a46bcdce611309463f5cc91749f9cc8efaac6402, rebased onto 0563666b09a09b2de039507307d38239dabee272.
  • Locked install/build; full-repository pnpm format; 180 focused tests passed. Publication tests use default CLI/MCP ensured clients and SDK HTTP transport against the public daemon runtime; HOME/USERPROFILE remain isolated.
  • Exact-head affected gate: passed, run-0VMDJP, exactCommit=true, checkoutChanged=false; nine checks and 3,601 tests passed, seven parked/GitHub-owned skips. Self-reviewed; no independent review or live device run for this rebase.
  • Quiesce counts dispatch through binding cleanup, so a held cleanup refuses quiesce as daemon-busy without acquiring its execution lock.
  • CI at prior head d7bdb0d70e: guards, integration, coverage, lint/package, docs, preview, size, native fingerprint, CodeQL and two smoke jobs passed; two smoke jobs queued, cache matrix skipped, cubic neutral. Current-head CI, including native smoke, remains required.

View guided diff Turn on auto-fix

@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.

No issues found across 7 files

You’re at about 91% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

View guided diff | Turn on auto-fix | Re-trigger cubic

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.34 MB 5.34 MB +631 B
Package (unpacked) 5.34 MB 5.34 MB +631 B
Package (download) 1.61 MB 1.61 MB +154 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 18.7 ms 18.8 ms +0.1 ms
CLI --help 52.4 ms 50.6 ms -1.7 ms

@thymikee

Copy link
Copy Markdown
Member Author

The PR is ready. I reviewed commit 49a4033 and found no blocking problems in the code.

Not blocking: these notes can be taken or left. (1) The Unreleased section of CHANGELOG.md has no entry for two user-visible changes: authored and --record'ed snapshot steps now persist in healed scripts (session-script-writer.ts#L315-L321), and requests that share a session or device now queue behind binding disposal. (2) session-action-recorder.ts#L89 stores publicationIntent: 'explicit-record' from flags.record, but sanitizeFlags already keeps that flag on the same action. Could the writer read action.flags.record and keep only the authored-provenance bit? (3) The PR joins two fixes, lock retention and snapshot publication intent. A line in the PR body on why they ship together would help. (4) The lock-retention test in request-runtime-binding-router.test.ts#L95 waits one setImmediate tick for the queued bind call. Any real async work before bind would let it pass without the fix. Could you wait on a deterministic signal, such as the second request entering the lock queue, and show the test failing at merge-base 4c1c786 with the scope change reverted? I did not run it there. I also did not read the per-provider binding disposers, and I did not check whether the .ad parser accepts --record on a snapshot step.

I found no conflicts. The iOS Smoke Tests job failed at "wait for the deep-link destination (5/5)" with APP_NOT_RUNNING, reason wait_capture_stalled, which looks like a held deep-link launch. A lock or disposal fault would show as a delayed or failed request, not a runner-reported app-not-running. The recording changes do not touch that route. I did not compare against the flake history on main. The gesture-pan-duration.ad failure passed on retry. Please rerun the Smoke Tests job and add the CHANGELOG entry before merge.

thymikee added a commit that referenced this pull request Oct 17, 2026
Address #3760 (comment). Read explicit recording from the existing action flags, retain authored provenance, add the Unreleased notes, and synchronize lock tests with queue insertion.

@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 7 files (changes from recent commits).

You’re at about 95% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

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

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread packages/daemon/src/core/session-script-writer.ts Outdated
@thymikee

Copy link
Copy Markdown
Member Author

Addressed all four maintainer notes in eb30491:

  • Added both Unreleased entries; explicit recording now reads action.flags.record, retaining only authored provenance separately. Ordinary publication behavior stays covered.
  • Added the shared ADR-audit rationale to the body.
  • Replaced setImmediate with execution-queue insertion. The same router suite gives 6 failed/2 passed on merge-base 4c1c786968 with only the test copied, versus 8 passed on head. The missing predecessor lock causes each base failure; the disposable checkout is removed.

iOS diagnosis: the original artifact screenshot shows unanswered “Open in Agent Device Tester?” confirmation; runner APP_NOT_RUNNING becomes wait_capture_stalled. Independent run fails identically with execution-scope blob 21eef92c7a matching merge-base, without this lock change. Apple binding disposal is a no-op. The single rerun was cancelled while queued after this push.

Validation: five focused suites, 104 passed; affected gate run-XYy1Xj, 2,302 passed, seven checks passed/seven skips, exactCommit=true, checkoutChanged=false. Self-reviewed.

Head CI: lint/typecheck/package, size, native fingerprint and CodeQL passed; smoke, guards, integration, coverage and cubic pending. Not merge-ready yet.

Review threads: 0 fixed / 0 already fixed / 0 rejected (none exist).

@thymikee

Copy link
Copy Markdown
Member Author

This PR is ready at eb30491. The earlier review of 49a4033 was clean, and this head keeps that result: cleanup locks are now retained and healed snapshots are preserved.

Not blocking: the flags.record !== true && guard in https://github.com/callstack/agent-device/blob/eb30491/packages/daemon/src/core/session-action-recorder.ts#L89 does nothing in a repair-armed session, because the writer already retains the snapshot there, so should authored provenance come from interactiveObservation alone (or can you say why authored plus --record should be stripped outside repair)? Also, publicationIntent?: 'authored' in https://github.com/callstack/agent-device/blob/eb30491/packages/contracts/src/session-action.ts#L32 acts as a boolean and could be collapsed the next time you touch it. You can take or leave both.

The Smoke Tests failure looks unrelated to this change. The log I have is from run 39008383674, which is not from this head. It shows the iOS simulator deep-link destination wait stalling (APP_NOT_RUNNING, wait_capture_stalled, zero readable captures), and the hint points to a deep-link confirmation prompt holding the launch. That is a simulator prompt state. The scope-disposal reorder and noRecord: true on the composed open-snapshot touch the same route, but neither answers prompts or launches apps. I did not run the router test or the replay-repair suites, and I did not check your claim about identical symptoms on the merge-base. The lock-ordering test spies on locks.set, so it ties to the current lock implementation.

Please rerun Smoke Tests on eb30491. Before merge, that run needs to be green, or show that the wait_capture_stalled prompt state comes from the environment. No conflicts are known.

On the open review threads, the P2 about record being settable from projectConfig (#3760 (comment)) does not apply, so you can resolve it. The recorder already treats effective flags.record === true as the force-record opt-in, and the writer mirrors that only in a repair-armed session. Dropping the snapshot would contradict the flag's documented meaning, and the kept step is a harmless replayable snapshot. That thread still needs your reply or dismissal.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 17, 2026
thymikee added a commit that referenced this pull request Oct 17, 2026
Distinguish explicit CLI/MCP/SDK corrections from project record defaults before recording and publishing repair observations. Retain authored provenance even when record is also set.

Review thread: #3760 (comment)
@thymikee

thymikee commented Oct 17, 2026 •

Copy link
Copy Markdown
Member Author

Fixed the config-derived recording leak in 73faf74. sanitizeFlags keeps effective record; CLI/MCP now preserve explicit provenance at ingress, and publication uses durable intent. Authored provenance no longer depends on record. Help, changelog, and replay docs are updated.

Regression before fix: 1 failed / 1 passed (unwanted snapshot). CLI/MCP/SDK publication coverage after fix: 5 passed. Provider integration exercises SDK ingress and rejects an effective flag without intent.

iOS: attempt 1 failed in daemon startup (daemon_startup_failed, 15s), before RPC dispatch; it was neither the earlier launch prompt nor queued binding cleanup. The single rerun passed startup/Settings/gesture, then failed step 84: native simctl openurl exited 60. The prior snapshot finished 234 ms earlier; 11.116s was spent inside simctl, excluding cleanup-queue timeout. Screenshot shows Home. iOS remains red.

At eb304913e7, Android/Linux/macOS smoke, lint/typecheck/package, guards, integration, coverage, size, native fingerprint, CodeQL and cubic passed; cache build matrix skipped.

Validation: pnpm format; 150 focused tests; provider/help suites 47 passed; affected gate run-qlYwKs: 9 checks / 3,598 tests passed, exactCommit=true, checkoutChanged=false; seven parked/GitHub-owned skips. Self-reviewed; no local device run.

Threads: 1 fixed/resolved, 0 already fixed, 0 rejected. New-head CI remains required.

@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://callstack.github.io/agent-device/pr-preview/pr-3760/

Built to branch gh-pages at 2026-10-17 11:09 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@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 17 files (changes from recent commits).

You’re at about 96% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

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

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread src/__tests__/cli-record-publication.test.ts Outdated
thymikee added a commit that referenced this pull request Oct 17, 2026
Set HOME and USERPROFILE together for CLI and MCP publication setups.

Review: #3760 (comment)
@thymikee

Copy link
Copy Markdown
Member Author

Pushed 81e2eba: CLI and MCP publication tests now set HOME and USERPROFILE together. Audited all other tests changed by this PR; no other new home-isolation gap found.

Threads: 1 fixed/resolved, 0 already fixed, 0 rejected. Re-read issue comments, reviews and inline threads after pushing; no unresolved threads or new maintainer action items remain.

Validation: pnpm format; publication suite 5 passed; exact-head affected gate run-WyTI9V: 9 checks / 3,598 tests passed, exactCommit=true, checkoutChanged=false; seven parked/GitHub-owned skips. Self-reviewed.

CI: at 73faf746cf, Linux smoke, size, docs, preview, native fingerprint, CodeQL actions/Java/Python and cubic passed; lint/package, guards, integration, coverage, CodeQL JS and Android/macOS/iOS smoke remained pending. No new failure log was available to diagnose. Cache matrix and aggregate CodeQL skipped. Current-head CI is queued/in progress, including iOS; this is not merge-ready.

T3 rejects CI watching from a sub-agent; the parent thread must continue monitoring and diagnose any failures. The prior simctl openurl exit-60 cause remains unproven. PR validation body updated.

thymikee added a commit that referenced this pull request Oct 17, 2026
Address #3760 (comment). Read explicit recording from the existing action flags, retain authored provenance, add the Unreleased notes, and synchronize lock tests with queue insertion.
thymikee added a commit that referenced this pull request Oct 17, 2026
Distinguish explicit CLI/MCP/SDK corrections from project record defaults before recording and publishing repair observations. Retain authored provenance even when record is also set.

Review thread: #3760 (comment)
thymikee added a commit that referenced this pull request Oct 17, 2026
Set HOME and USERPROFILE together for CLI and MCP publication setups.

Review: #3760 (comment)
@thymikee
thymikee force-pushed the fix/adr-drift-lock-and-heal branch from 81e2eba to d7bdb0d Compare October 17, 2026 08:19
@thymikee

Copy link
Copy Markdown
Member Author

Rebased 81e2eba0f1 onto current origin/main (3404cfc413); pushed head d7bdb0d70e with an explicit force-with-lease.

Resolved both test conflicts against main’s history: retained its typed action-history cases and the PR’s publication-intent cases; kept provider scenarios on public runtime/HTTP results and healed-script assertions. Moved the five CLI/MCP/SDK publication cases to provider integration through the public daemon runtime, removing all three new daemon testing-export imports. Production changes are unchanged by the rebase.

Validation: locked install/build, full-repository format, 120 focused tests passed. Exact-head affected receipt run-oBaplK: 9 checks / 3,570 tests passed, exactCommit=true, checkoutChanged=false; seven parked/GitHub-owned skips. Current-head GitHub CI remains required. Existing review fixes and HOME/USERPROFILE isolation are preserved.

thymikee added a commit that referenced this pull request Oct 17, 2026
Address #3760 (comment). Read explicit recording from the existing action flags, retain authored provenance, add the Unreleased notes, and synchronize lock tests with queue insertion.
thymikee added a commit that referenced this pull request Oct 17, 2026
Distinguish explicit CLI/MCP/SDK corrections from project record defaults before recording and publishing repair observations. Retain authored provenance even when record is also set.

Review thread: #3760 (comment)
thymikee added a commit that referenced this pull request Oct 17, 2026
Set HOME and USERPROFILE together for CLI and MCP publication setups.

Review: #3760 (comment)
@thymikee
thymikee force-pushed the fix/adr-drift-lock-and-heal branch from d7bdb0d to a46bcdc Compare October 17, 2026 08:52
@thymikee

Copy link
Copy Markdown
Member Author

Rebased d7bdb0d70e onto origin/main (0563666b09); pushed a46bcdce611309463f5cc91749f9cc8efaac6402 with an explicit force-with-lease.

Resolved CHANGELOG.md by keeping both sets of entries in main’s order/format. Resolved MCP by keeping main’s ensure-then-attach transport and the PR’s recordSource plumbing. CLI/SDK auto-merges preserve intent. Found and fixed a test gap: publication tests now use default CLI/MCP ensured clients and SDK HTTP delivery, rather than injected transports/clients (a46bcdce61).

Quiesce remains compatible: a request stays in flight through binding cleanup, so quiesce returns daemon-busy without acquiring the held execution lock. No production change needed.

Validation: locked install/build, full-repository format, 180 focused tests passed. Exact-head receipt run-0VMDJP: 9 checks / 3,601 tests passed, exactCommit=true, checkoutChanged=false; seven parked/GitHub-owned skips. Self-reviewed; no live device run. PR validation body updated. Current-head CI remains required.

Review threads: 0 newly fixed / 0 already-fixed actions / 0 rejected; both existing threads remain resolved.

@thymikee

Copy link
Copy Markdown
Member Author

The earlier review at eb30491 was clean, and the changes since then fix both inline issues. At a46bcdc the writer keys on explicit-record intent, which comes only from the CLI or MCP ingress, so a project-default record no longer opts in. The integration test now sets HOME and USERPROFILE together. The code looks right to me, but I did not run the new integration tests. I reasoned about the regression from the code at eb30491. Current-head Smoke Tests are still queued or running with no failure so far, so the evidence is pending. Both jobs need to finish green before merge. The diff touches CLI request-flag construction and the daemon's repair-publication filtering, and the smoke runs of ordinary commands should not reach that route.

Not blocking, and you can take or leave these: the new recordingIntent flag in command-flags.ts and the recordSource dependency threaded through the client, MCP and cli.ts carry the source of record to the daemon, which now ignores flags.record, so a configured record: true has no effect. The docs line and the CHANGELOG name only the project-config default, but env and user-config defaults are also non-explicit, so "any config or environment default" would be accurate. I also did not check whether a project-config record: true still writes --record into recorded script lines through sanitizeFlags, so the key may not be fully dead.

Could record be invocation-only at the config owner instead? That means dropping projectConfig: true from its declaration and skipping it in readEnvFlagDefaults, the user config and resolveMcpConfigDefaults. Then flags.record === true is always explicit, the daemon can read it again, and recordingIntent, both recordSource deps and the cli.ts stamp go away. The one thing to settle first is whether to retire the released project-config key (in tags since v0.21.10) with a CHANGELOG entry. It already has no effect at this head, so retiring it removes dead surface instead of breaking a working one.

Of the earlier inline threads, the one on the writer opting in from a project default and the one on HOME and USERPROFILE in the test no longer apply at a46bcdc, so you can resolve both. No review has covered this head from that reviewer yet. No conflicts are known.

@thymikee thymikee removed the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 17, 2026
ADR 0019 requires request bindings to dispose before device/session lock release. Await the shared cleanup outcome under initial and retained execution locks, then let response finalization preserve primary failures and report cleanup failures.

At base 4c1c786, six blocked-cleanup regressions fail: queued same-session or same-device requests enter on success, throw, and cancellation. With the fix all six pass; the scope and open-wait suites pass (42 tests). No request-binding disposal bound exists; no timeout was added. Device dispatch is unchanged.
ADR 0012 requires authored observations and explicit --record corrections to survive healed-script publication. Carry their typed publication intent from recording into the writer instead of stripping every snapshot. Retain ordinary diagnostic-snapshot optimization.

Isolated regression at base 4c1c786: authored and explicit-record persisted snapshots and the correction-count/resume publication scenario all fail (3 regressions); all pass with the fix. Combined before: 9 failed, 43 passed; after: 52 passed. Recorder/resume focused coverage also passes (82 tests). Device dispatch is unchanged.
ADR 0012 decision 6 preserves authored observations through replay provenance. Non-observation wait and diff requests must not acquire authored intent from their false observation classification.

Regression proof on 17b30bf plus tests: 2 failed, 31 passed (interactive wait/diff received authored intent). With the fix: 33 passed.
ADR 0012 decision 6 preserves the authored replay path. The initial snapshot composed by open --foreground is not a source step and must not inherit its recording intent, including a configured record flag.

Regression proof on 17b30bf plus tests: 2 failed, 13 passed because composite snapshots entered session.actions. With noRecord on the synthetic capture: 15 passed; capture response remains successful.
ADR 0012 decision 6 requires authored observations to survive the entire repair publication path. Exercise a source snapshot -i through replay, close, healed-file parsing and fresh-session replay.

Isolated regression: restore the writer from dacec4a (before snapshot retention) in the 17b30bf checkout plus this test. The selected case fails because the healed snapshot is missing (1 failed, 5 skipped). With retention: 1 passed, 5 skipped; full repair-recording suite: 6 passed.
ADR 0012 decision 6 scopes --record observation retention to repair-armed sessions. Preserve ordinary authoring snapshot exclusion even when project configuration supplies record=true; keep authored source observations and explicit repair corrections.

Regression proof on 17b30bf plus tests: 1 failed, 1 passed, 39 skipped (ordinary record=true published a diagnostic snapshot). With the fix: 2 passed, 39 skipped. All four focused suites: 95 passed.
Address #3760 (comment). Read explicit recording from the existing action flags, retain authored provenance, add the Unreleased notes, and synchronize lock tests with queue insertion.
Distinguish explicit CLI/MCP/SDK corrections from project record defaults before recording and publishing repair observations. Retain authored provenance even when record is also set.

Review thread: #3760 (comment)
Set HOME and USERPROFILE together for CLI and MCP publication setups.

Review: #3760 (comment)
@thymikee
thymikee force-pushed the fix/adr-drift-lock-and-heal branch from a46bcdc to 9918f7e Compare October 17, 2026 11:08
@thymikee

Copy link
Copy Markdown
Member Author

Rebased a46bcdce61 onto origin/main (a61556b8fd); pushed 9918f7e67ae8108095fc121aa250cd2fe31659c4 with force-with-lease. Only CHANGELOG.md conflicted (from #3773); kept both entry sets in main's order and format. No other conflict; production diff unchanged.

iOS Smoke root cause: environmental, not this PR. At run 39019020372 the simulator boot's data migration ran into the preflight window (bootstatus terminal at 09:28:28, clean:daemon still running to 09:29:49 on a saturated runner), and the spawned daemon (pid 6849, 09:30:03) was starved past the fixed 15 s daemon_startup_failed timeout with a zero-byte daemon.log. The identical daemon_startup_failed / 15 s / attempts=1 preflight failure hit unrelated branches that contain none of this PR's commits: refactor/host-environment-root 39008970059 (06:23:02) and fix/provider-not-registered 38993655494 (02:11:26), same host pattern. The PR touches no daemon startup/spawn path (git diff --name-only origin/main...HEAD matches none of src/daemon-client, packages/daemon-contracts, src/bin.ts); its daemon changes run per-request in request-execution-scope.ts. Local check on this head: fresh-daemon start writes daemon.json in ~0.7 s (3/3), 44 daemon startup/lifecycle tests pass. Attempt-2's simctl openurl exit 60 is the known deep-link prompt state, matching the reviewer's environment diagnosis. No code fix; no regression test warranted.

Validation: locked install/build, full-repo format, 120 focused tests passed. Exact-head receipt run-KxKbMv: 9 checks passed, exactCommit=true. New-head CI (run 39027567868) remains required. Both review threads resolved; the not-blocking prose/record-scoping suggestions from the 09:43 review are recommendations for the maintainer — the invocation-only record redesign is out of this PR's scope.

This branch has not been deployed

No deployments
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