mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-22 17:22:47 +00:00
* Wait for plugin reload completion instead of the request deadline The daemon answers plugin.reload.request only once the plugin is running again or has failed, and a reload queues behind other plugin lifecycle work such as an install with a preparation command. The shared client applied its sixty-second session RPC deadline to that request, so a slow reload failed with "Timeout waiting for message" while the daemon kept loading the plugin, inviting a reload the daemon was still running. Reload now waits for the daemon's own answer. Enable and disable keep the session deadline, and a real startup failure still reaches the caller. * Cover a slow plugin reload end to end The CLI fixture now holds its own git build on a gate file, starts a reload of an installed plugin behind that queued install, observes the reload request at the proxy, keeps the gate closed past the client's former one-minute deadline, and then asserts the reload's real result: CLI exit 0, the plugin running, and exactly one reactivation. Listing the plugin during the wait stays readable and does not restart it. A plugin that cannot start still fails its reload with the daemon's error. The request proxy that recorded install sources becomes a small observer that records any session request, so the wait is measured from the request boundary rather than from process start. Verified: npx tsx packages/cli/tests/e2e/plugin-lifecycle.test.ts exits 0 in 135 s; the same fixture without the new scenario takes 66 s. * Document how a reload reports its result The CLI docs now say `paseo plugin reload` waits for the daemon to stop and start the plugin, that cancelling the command or losing the connection does not stop the daemon's work, and that `plugin ls` shows the previous state until the reload finishes. The plugin reference records the lifecycle fact behind it: a reload queues behind the daemon's other plugin work, the daemon answers when the plugin is running again or has failed, and status reads, Settings and the logs are not the reload's outcome. * Cover the Settings reload wait end to end Adds a browser scenario for the ticket's user-facing half: with a gated git install holding the daemon's plugin lifecycle queue, the row's Reload goes out, the test keeps the gate closed past sixty seconds, asserts the row stays pending with no error feedback, then releases the build and asserts the daemon's plugin.reload.response and the app's "Reloaded e2e-plugin" result. The scenario turns plugins on through the daemon instead of the global switch, whose transient "Plugins enabled" confirmation is not its subject. The wait is measured from the reload request frame, which is an outbound frame, not from the response stream. Verified: npx playwright test --project=browser e2e/browser/plugin-management.spec.ts -g "keeps Reload pending past a minute" -> 1 passed (1.5m); plugin-reload-pending.png, plugin-reload-past-deadline.png and plugin-reload-completed.png show the pending row, the surface past the former deadline with no error, and the finished reload. * Correct the status guidance for a cancelled reload Review finding: the note said a listing keeps the plugin's previous state until the reload finishes, but that only holds while the reload is still queued. Once it executes, the daemon removes the plugin from the runtime catalog before stopping it (plugins/runtime.ts:377-382) and resolvePluginStatus maps an enabled plugin that is not running to "failed" (plugins/index.ts:71-78), so a listing read in that window shows failed for a healthy reload. Both passages now separate the two windows and say that neither a listing nor the logs report the outcome of the outstanding reload. The interim "failed" itself is TUC-1401. * Harden the reload coverage against failures and lost connections Review findings folded into the tests: The Settings scenario releases its gated build in a finally block. Before this, a failing assertion left the build polling forever, the daemon's plugin lifecycle queue stayed blocked, and the fixture teardown's own removePlugin timed out behind it. It also asserts the row's actions are enabled again once the reload settles, next to the result feedback. The connection-close regression now reconnects and asserts nothing resends the lost reload; the no-replay invariant already covered for legacy requests now holds for a pending reload too. Verified: npx vitest run packages/client/src/daemon-client.test.ts -> 161 passed. npx playwright test --project=browser e2e/browser/plugin-management.spec.ts -g "keeps Reload pending past a minute" -> 1 passed (1.4m). * Let the gated build finish before its files go Review finding on packages/cli/tests/e2e/plugin-lifecycle.test.ts:450: the finally block released the gate and deleted the gate directory without waiting for the install and the reload it had started. The build polls for the gate file, so cleanup could delete the release before the child saw it and leave the build waiting until daemon shutdown. The fixture now keeps both promises, releases the gate, and awaits them before deleting their files. Verified: npx tsx packages/cli/tests/e2e/plugin-lifecycle.test.ts -> exit 0, 161 s. * Read the reload scenario as intent, not transport code Review finding: the new browser scenario kept frame parsing, selectors, polling, deadline arithmetic, screenshots and gate cleanup in one body. docs/testing.md asks for complex setup in reusable helpers. The wire observation, the gated queue hold, the row-state assertions, the deadline wait and the surface captures are helpers now, and the body reads as the flow: open Settings, hold the queue, reload, still pending past the deadline, release, read the daemon's result. Verified: npx playwright test --project=browser e2e/browser/plugin-management.spec.ts -g "keeps Reload pending past a minute" -> 1 passed (1.4m). * Keep the reload lifecycle fact in one document Review finding: the cli.md paragraph repeated the queue and interim-state explanation that the plugin reference already owns. CLAUDE.md: "One fact, one doc. Every other mention is a link." The reference keeps the full explanation; the CLI page states what the command does and links there. * Release the gated build when its setup fails Review finding: holdPluginLifecycleQueue started the install, then waited up to 30 seconds for the build's started marker. A preparation that took longer made the helper throw before it returned its cleanup handle and before the test entered its try block, so nothing wrote the release file: the build polled forever, the daemon's plugin lifecycle queue stayed blocked, and the fixture's own removePlugin timed out behind it. The helper now owns that failure path: it releases the gate and drains the install before rethrowing, so a queue it hands out is always settleable. Verified on Linux (Chromium, Playwright): - With the started marker suppressed in a temporary local patch, the run fails at the 30-second marker wait inside the helper and the teardown still completes (both attempts ~33 seconds, no removePlugin request timeout). - With the marker in place, the scenario passes: 1 passed (1.4m). * Keep an early install failure out of the unhandled path Review finding: the marker wait in holdPluginLifecycleQueue ran before the install promise had a rejection handler, so a preparation that failed or a connection that closed before the build wrote its marker surfaced as an unhandled rejection, which fails the run before the helper's own catch can drain the queue. The helper attaches a handler the moment the install starts and races the marker wait against the install, so an early failure reaches the caller as the daemon's own error, a build that never writes its marker still fails as a 30-second marker timeout, and every path releases the gate and drains the install. Verified on Linux (Chromium, Playwright), patching the build command locally for each failure branch: - Early failure (build exits 1): fails in 4.2 s with the daemon's DaemonRpcError for plugin.source.install.request, no unhandled rejection, teardown completes. - No marker (build waits for the gate): fails at the 30-second marker wait, no unhandled rejection, teardown completes with no removePlugin request timeout. - Unpatched: 1 passed (1.5m). * Keep a failed launch from ending the run before its cleanup