Skip to content

perf(daemon): lazy-load Maestro in request binding; budget the daemon entry - #3284

Merged
thymikee merged 4 commits into
mainfrom
perf/ws6-daemon-cold-start
Oct 7, 2026
Merged

thymikee merged 4 commits into
mainfrom
perf/ws6-daemon-cold-start

Conversation

@thymikee

@thymikee thymikee commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Summary

Closes #3282 (part of #3276). 4 files.

  • Maestro, now lazy. Request binding (src/daemon/replay-device-selection.ts) imports @agent-device/maestro through a function-scoped await import, reached only when the replay entry resolves to a Maestro flow. .ad replays and other commands never load it. In the built output, daemon boot drops exactly 109 modules (37 maestro + 72 yaml): 209→201 files, 2.33→2.17 MB; a sourcemap diff shows no other module moves.
  • Gate. src/daemon.ts joins HUB_ENTRY_FILES, under the merge-base no-growth rule (621→571 here).
  • provider-webdriver, not changed. startDaemonRuntime awaits createDaemonProviderRuntimeComposition → createDefaultRuntimes(env) before it listens (daemon-runtime.ts:361 vs :602). A dynamic import loads the same chunk in the same phase and only lowers the static count; the chunk evaluates in ~1.4 ms, too little to justify making runtime construction lazy.

Validation

Commit 651c22ccb: AGENT_DEVICE_VITEST_MAX_WORKERS=2 pnpm check:affected --run — all runnable checks passed. CI at this head: all jobs green, incl. Compatibility & Provenance and live web smoke.

  • Planted regression. Restoring the static import fails the src/daemon.ts hub-row no-growth check (621 vs 571, route printed); without the row, the plant passes.
  • Boot (built daemon, interleaved, n=40): spawn→ready median 149.4→140.2 ms.
  • Maestro first request (n=30): spawn→first replay response 543.3→536.6 ms; the chunk costs +4.9 ms once.
  • BrowserStack lease_allocate (n=20): cold 297→295 ms, warm 86→87 ms.
  • Built chunks resolve at runtime: a maestro replay reaches step 1 (DEVICE_NOT_FOUND); lease_allocate reaches the credential check.
  • Review follow-ups. Malformed wire bundles stay advisory (replay cannot take a path against a remote daemon #1802): whole probe inside readAdvisoryResolution, test mutation-proven at the pre-fix shape. Fallow complexity fixed by decomposition, no baseline change.
  • Web-smoke hang at b27e310: not reproducible, not this change. The lane passes locally on the built output (n=2) and on CI at f9c9b82 and 651c22. Its artifact shows web setup stalled mid managed-install download (no manifest.json, no daemon ever started). web setup is CLI-local and never starts a daemon, so the lazy branches are off-path. Runner-network flake.
  • Risk. The CLI's 100 ms readiness poll can hide the boot gain for CLI-launched daemons.

🤖 Generated with Claude Code

thymikee and others added 2 commits October 7, 2026 15:27
…a flow

Request binding sits in the daemon's startup closure, so its static import of
@agent-device/maestro evaluated the engine and the YAML parser (37 maestro +
72 yaml modules in the built output) on every daemon start. The flow is now
inspected behind a function-scoped import reached only once the replay entry
resolves to Maestro; native .ad replays and every other command never load it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
src/daemon.ts is what every daemon start evaluates, yet no budget row watched
it, so a static edge back into Maestro would have regrown startup silently.
As a hub it now falls under the merge-base no-growth rule.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.12 MB 5.12 MB -3.1 kB
Package (unpacked) 5.12 MB 5.11 MB -3.1 kB
Package (download) 1.54 MB 1.54 MB -1.9 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 26.8 ms 27.1 ms +0.3 ms
CLI --help 83.0 ms 82.4 ms -0.5 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 4 files

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

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

Comment thread src/daemon/replay-device-selection.ts Outdated
@thymikee

thymikee commented Oct 7, 2026

Copy link
Copy Markdown
Member Author

This PR is ready: the change looks right at f9c9b82, and the lazy Maestro load in request binding does what the title says.

Not blocking: the new Maestro test in replay-device-selection.test.ts checks that the lazy branch still pre-binds, but not that the module stays unloaded for .ad replays (the eager-closure budget row enforces that), and the redundant toBeDefined() after toEqual could be dropped. You can take or leave this.

One open thread from another reviewer still applies: malformed bundle entry is low priority. It only matters for a malformed wire payload, where a non-string bundle.entry would now throw out of resolveFreshSessionDevice. Computing the format inside the same try as the advisory read would close it.

The Linux Smoke Tests job failed in the apt-get dependency install step on a timeout, before any repo code ran, and this diff does not touch that step, so it looks unrelated. Please re-run that job. Coverage and two other Smoke Tests jobs were still running when I looked, so they need to finish green before merge. I did not recount the daemon closure, re-run the planted regression, or re-measure startup latency.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 7, 2026
Making the Maestro engine lazy hoisted the bundle-entry read and format
resolution out of the try that tolerates invalid replay plans (#1802).
The HTTP boundary validates flags only as an object, so a wire request
can carry a bundle whose entry is missing or non-string, and
path.extname would then reject the request with a TypeError during
advisory lock selection instead of falling back to normal device
resolution. Wrap the whole probe in the advisory guard again; the
engine import stays inside it.
…elper

Keeping the whole probe inside the advisory guard grew its closure past
the fallow complexity threshold. Split the maestro and .ad plan reads
into named helpers so the guarded closure only resolves the bundle
entry and routes by format; the #1802 tolerance stays in
readAdvisoryResolution alone.
@thymikee

thymikee commented Oct 7, 2026

Copy link
Copy Markdown
Member Author

This PR is ready. The code at 651c22c is clean, and the earlier review of f9c9b82 is addressed. The new try block now covers the bundle.entry and resolveReplayFormat calls, and the new test covers entry undefined and entry 42.

All 19 checks pass at 651c22c. I did not run the tests locally. I judged the regression coverage by reading the f9c9b82 code path.

Not blocking: in https://github.com/callstack/agent-device/blob/651c22c/src/daemon/replay-device-selection.ts#L52 the await import('@agent-device/maestro') now sits inside the catch-all guard, so a failed module load (for example a broken install) returns undefined silently during lock binding, where a static import used to crash daemon startup. You can take this or leave it, and leaving import failures to the handler would match what the comment already says.

The cubic-dev-ai P3 thread on replay-device-selection.ts lines 37-44 is fixed at this commit, so please resolve it: #3284 (comment)

Nothing else is needed before a human merge.

@thymikee
thymikee merged commit 159f489 into main Oct 7, 2026
19 checks passed
@thymikee
thymikee deleted the perf/ws6-daemon-cold-start branch October 7, 2026 16:38
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-07 16:38 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Architecture WS6: lazy-load maestro and provider-webdriver branches in daemon cold start

1 participant