AGENTS.md@desktop · git:20261103.099dea5 · 2026-11-03 · sha256 6834798d9e9cd928
AGENTS.md@desktop git:20261103.099dea5A
Immutable. This exact content never changes and is served at /api/v1/blob/6834798d9e9cd928.
# Desktop Agent Notes ## Frontend And Host Compatibility - The desktop frontend and host are versioned and shipped together. Target a matching frontend and host version, and evolve protocol payloads and both implementations in the same change. Support for mixed versions is a separate product requirement when explicitly requested. - The relay serves one current browser frontend to every host. When a host command, reply or event changes incompatibly, increment `HostProtocolRevision` in `protocol/desktop_messages.mbt` (and the E2E harness's `hostProtocol`). The host sends it in every `agent.connected`. A browser starts its automatic resync only after a matching revision; on a mismatch it closes the socket without retrying and shows the host as unavailable on every non-Cloud page. Requests are not gated at the transport, so a manual read in the moment between the socket opening and `agent.connected` (opening Skills or Scheduled, say) can still reach a mismatched host before the gate covers the page. Keep `agent.connected` decodable for every host: add fields, but never require one beyond `protocol`. ## Module Boundary - `desktop/backend` is a library whose direct and transitive dependencies are independent of Proton and window toolkits. Code that needs Proton belongs in `desktop/app`; when shared code needs a fact only Proton knows, the window host passes it in as a value (`@resources.Resources`). - `desktop/app` is the only module that depends on Proton. It is left out of the root `moon.work` and builds in its own `desktop/app/moon.work`, so root Moon commands (and the CI rows that run them) need no Proton runtime. Keep Proton imports in this module; `proton_contract` is the exception, being target-neutral types with no native setup. - `desktop/app` is a thin host of the backend. Put services required by every backend host in `desktop/backend` (`ApiState::start_services`); keep window initialization in the app's entry point. - Packages the window host imports sit at the top of `desktop/backend`; the rest stay under `desktop/backend/internal`. Moon allows an `internal` import only inside one module, so moving a package out of `internal` is what exposes it to another module. Make that move when the task requires a public host-facing package contract. ## Development Builds - The supported development build is the packaged `.dev` application (`package/build.mjs` with the development project config, e.g. `dist/SeekMoonDev.app`). It runs the same packaged layout as a release — `seekmoon/web` for the frontend, the per-user runtime dir for state — and differs only by its identity suffix and the development-only behavior gated on it. - For behavior that depends on the Desktop host or packaging, reproduce and validate it in the packaged development app. Unpackaged launch paths are historical; their removal belongs to a task that includes that cleanup. ## Validation By Change Scope Apply the [root scope and expansion rules](../AGENTS.md#validation-by-change-scope). | Change | Targeted checks | | --- | --- | | CSS or presentation assets | Rebuild the browser bundle and preview affected UI or run a focused Playwright spec. | | Frontend MoonBit source | JS checks and relevant model/package tests; browser cases for changed rendering, events, or user workflows. | | Protocol/codecs or backend behavior | Owning package and affected consumer checks/tests; exercise changed frontend/backend flows. | | Proton window host or contracts it consumes | Check/test affected packages from `app/moon.work`; validate host-dependent behavior in the packaged development app. | | Build scripts or packaging | Affected script syntax/tests or build-package checks, then inspect the affected artifact. | Command details: [browser tests](e2e/README.md), [host checks](README.md), and [packaging](package/README.md). Browser fixtures replace HTTP/WebSocket sources; use real backend/host checks when those layers change. Full entries from the repository root: `just desktop-test-browser`, `just desktop-app-check`, `just desktop-app-test`, and `just desktop-build-scripts-check`, when justified or explicitly required. ## UI Pull Requests - For PRs with visible Desktop UI changes, include screenshots in the PR description showing the affected UI from the changed build. Include before-and-after comparisons when useful, and a short recording when the change is best demonstrated through interaction. - Upload UI screenshots and recordings as PR attachments. Keep local captures outside the repository or in an ignored directory. - Prefer demo data when capturing screenshots or recordings. Before uploading, remove or redact personal information, real conversations, private project names and paths, credentials, and other sensitive content, including content in sidebars, terminals, and notifications. - Inspect the final images or recording before uploading to confirm that sensitive content is removed and the UI change remains clear. If visual evidence cannot be captured, explain the limitation in the PR description. ## Child Processes - For a one-shot command that collects stdout and stderr and expects EOF on stdin, use `@processx.collect_output_no_stdin` from `desktop/backend/internal/processx`. - `collect_output_no_stdin` gives the child a valid stdin handle that is already at EOF and always suppresses creation of a console window. Both are required for Windows GUI builds: inheriting a missing standard-input handle makes process creation fail, while launching a console application without suppression flashes a visible console window. - Interactive commands and long-lived protocol processes use their owning lifecycle APIs and the stdin pipe, file, or PTY required by their protocol. ## JSON Protocol Codecs - When adding a JSON type or changing its JSON contract under `desktop/`, write explicit `FromJson` and/or `ToJson` implementations for the serialization directions it uses. Update the affected codecs in the same change. - A decoder defines the treatment of required, missing, `null`, malformed, and unknown fields. An encoder defines which optional fields are omitted. Follow the owning protocol's field semantics and error behavior. ## Command Validation - Use typed requests and responses to check payload shape and expose schema drift. Enforce the affected command's documented product constraints. - For additional guards against user mistakes or concurrent operations, identify the supported UI actions that reach the state, the defined constraint they would violate, and the expected UI result. Include multiple browser clients and hosts when those actions can reach the same data. Implement the guard within the requested feature's scope. ## Desktop Trust And Security Model - The local user, processes running as that user, and authenticated Desktop frontends share the user's command authority, including terminal and shell access. This covers the Proton renderer, authenticated relay clients, and code the user deliberately builds or executes. Workspace and path checks express product scope within that authority. - Authenticate relay frontends before granting a Desktop command channel. The development loopback console grants local access without a token or cookie. Keep unrelated web origins and content processed as data outside that authority. Rendered Markdown, diffs, file previews, tool output, and model output remain data, including when they originate in a checkout. Distinguish deliberate code execution from displaying or parsing a file. - A security finding identifies the less-trusted principal, the boundary it crosses, and the new capability it gains. Evaluate these boundaries independently of which controls the normal UI exposes. - The development loopback console supports direct links and fresh browsers without an authorization step. Bind to loopback, validate `Host` against the bound address, and require any supplied `Origin` to match the console origin. These checks fence unrelated web origins without browser credentials.