# Architecture and invariants
Winx is a long-lived command-execution service, not a stateless collection of MCP functions. Its architecture keeps
transport, project identity, shell ownership, file evidence, and model-facing recovery separate so each boundary can
fail closed without losing useful sessions.
## Process model
On Unix, three executable roles cooperate:
```text
MCP client
|
v
winx-code-agent adapter: HTTP/stdio, auth, schemas, session routing
|
v
winxd control plane: quotas, discovery, guardian lifecycle
|
v
winx-guardian one durable PTY owner per logical project session
```
The adapter may restart without killing a guardian. The control plane may perform a planned restart while existing
guardians keep their PTYs. Every negotiated process reports a typed role and an exact build identity; capability checks
come from the effective guardian, never from the newer adapter or control plane by association.
All launch paths resolve the control socket through `daemon::socket`. An explicit `WINX_SOCKET` wins, then a private
`XDG_RUNTIME_DIR`, then `/run/user/<uid>`, and only then a private `/tmp` fallback. `doctor` inspects every candidate and
reports split-brain sockets, incompatible control builds, and mixed guardian builds without exposing credentials.
## MCP request path
The server path is deliberately layered:
1. `http_server` applies network limits, host validation, authentication, readiness, and request correlation.
2. `server::principal` scopes caller-controlled thread and Task identifiers to the authenticated principal.
3. `server::coherence` verifies the immutable conversation/workspace binding. Allowed external paths do not change the
project identity, even with `WINX_ALLOW_PATHS=/`.
4. `server::handler` owns protocol negotiation, Task routing, single-flight mutation coordination, and telemetry.
5. `server::tool_dispatch` deserializes a registered tool, lowers `EditFiles` and hidden compatibility aliases into the
unified mutation domain, and invokes the resulting operation under the correct logical session.
6. `server::outcomes` converts typed domain results into the shared MCP orchestration envelope.
`tool_registry::ToolKind` is the stable source of truth for tool names, discovery order, policy bits, annotations,
session contract, output contract, and exhaustive dispatch. Additions are appended so persisted policy bits retain their
meaning. Profiles in `tool_policy` are derived from those typed identifiers rather than parallel strings.
## Session and project coherence
`thread_id` and `workspace_root` form one project-session identity. A remote conversation cannot silently rebind that
identity to another project. A tool may intentionally access an allowed absolute path outside the root, but doing so
does not mutate the binding or make a different project's PTY current.
The adapter registry is bounded and pins in-flight sessions against eviction. The guardian owns the authoritative PTY,
cwd, command generation, output journal, activity clock, and runtime state. Model-controlled terminal text is never
parsed to infer whether a command is running, awaiting input, or completed.
Shell recovery is evidence-bound in the adapter. One explicit reset is permitted, then a five-minute live-session
cooldown preserves the replacement PTY unless `BashCommand` returned a typed runtime/infrastructure failure. A redundant
request completes as `reset_skipped_healthy` rather than destroying useful cwd, terminal, or process context. The guard is
intentionally not persisted: losing adapter state is itself a new recovery boundary, and guardian attach remains
observational.
Task cancellation is bound to that exact runtime identity. A cancellation that races a launch waits for either the
published generation token or proof that launch finished without a process. If the handshake cannot settle within its
bounded deadline, Winx terminates the affected session before acknowledging cancellation. It never lets a stale,
generation-less interrupt escape and target the next command.
## File evidence and mutation protocol
File reads use bounded, stable snapshots through safe standard-library APIs. A descriptor is read, metadata is checked
again, and a concurrent replacement retries from a fresh descriptor. There is no memory mapping or shared mutable view
of a live file.
`ReadFiles` returns both visible text and a machine-readable receipt for every successful file:
- canonical path;
- opaque SHA-256 revision;
- total line count;
- exact complete line ranges visible to the model;
- truncation and continuation metadata.
Token truncation retreats to a complete newline. Only complete visible lines become edit evidence, so a large-file read
cannot accidentally authorize an unseen overwrite.
Mutations follow one typed plan/commit boundary. The public `EditFiles` wire and hidden legacy compatibility aliases all
lower into `tools::edit_files`; there is one source of truth for cardinality, mode authorization, canonical targets,
receipt identity, and commit reporting. Planning
validates path policy, mode policy, read coverage, expected revision, and edit syntax while computing the new bytes in
memory. Commit revalidates both the canonical path binding and original bytes immediately before an atomic replacement.
A failed plan writes nothing. A commit-stage failure reports the committed prefix and untouched suffix rather than
claiming cross-file rollback after a write already became durable.
`EditFiles` mode `line_patch` is the preferred compare-and-swap path when the model already has line coordinates. All
patches refer to one original revision, are ordered and non-overlapping, and may touch only visible ranges. A stale replay returns an
exact `ReadFiles` recovery action and cannot mutate the newer file. SEARCH/REPLACE remains available for text-anchored
work and invalidates its read permit after a conflict. Because `ReadFiles` already returns both coordinates and a
revision, `line_patch` is the default existing-file path. SEARCH conflict recovery performs the exact structured read
and switches the corrected retry to `line_patch` instead of escaping to an untracked shell edit.
Successful mutations receive bounded persisted receipts. Identical concurrent calls are single-flight, and a lost
response may be replayed without repeating the write only while target postconditions still match. Verification is a
separate receipt-bound operation: a failed check never tells the model to repeat a committed edit.
Workspace-local derived helpers live only in `.winx/tmp/session-…`. Hard byte/file limits remain fail-closed. A separate
high-water policy controls long-lived active sessions: at 75% of either session limit, Winx may unlink only managed
artifacts whose metadata has been inactive for the full 24-hour TTL, oldest first, until 50% headroom is restored. The
bounded walk never follows symlinks; an unreadable or excessive tree is retained. Fresh helpers and canonical project
files are never candidates, and any remaining hard-limit overage still requires explicit cleanup.
## Errors and agent recovery
Domain code returns typed `WinxError` variants carrying paths, reasons, ranges, and revisions. `server::recovery`
classifies those variants directly; it never searches human-readable error strings. Expected correction flows are MCP
tool results with `isError: true`, `retrySameCall: false`, and an exact `nextAction` when one is safe to infer. Only
serialization, poisoned state, and comparable server failures become JSON-RPC internal errors.
This contract is part of the agent interface. Descriptions lead with the recovery rules, schemas avoid client-rejected
constructs, and trace fixtures assert that retries, fresh reads, committed edits, and workspace rejection remain safe.
## Observability and deployment
Operational logs and privacy-safe usage events are separate. Usage telemetry records fixed command categories, hashes
for correlation, result/recovery metadata, latency, response size, workspace coherence, and exact build identity; it
never records shell text, file contents, arguments, tokens, or raw credentials.
`winx-code-agent report` aggregates rotated JSONL offline, correlates tool and HTTP events, summarizes latency/bytes, and
audits retry invariants. Usage events carry a schema version so older logs with missing recovery fields are reported as
unverifiable instead of becoming false violations. `healthz` reports liveness; `readyz` turns unavailable during graceful drain. HTTP shutdown
stops admission before waiting for active work.
The versioned installer builds all three binaries together into an immutable bundle, smoke-tests the adapter, then
atomically switches symlinks. Existing processes retain their old inode; new launches cannot observe a partially
installed trio. The maximally optimized `dist` profile is reserved for published/versioned artifacts so normal release
development remains responsive.
## Module ownership
- `src/server/`: MCP lifecycle, catalog, policies at the request boundary, session routing, recovery, mutations, Tasks,
usage events, and response shaping.
- `src/tools/`: model-facing capabilities, compatibility adapters, and shared domain engines; all file mutations converge in `tools::edit_files`,
while larger capabilities keep focused parser/planner/commit/report submodules.
- `src/state/`: PTY, terminal rendering, persistent shell state, bounded journals, and edit/read evidence.
- `src/daemon/`: wire protocol, control plane, guardian client/server, lifecycle, and socket resolution.
- `src/runtime/`: one runtime abstraction over embedded and daemon-backed shell execution.
- `src/utils/`: safe snapshots and reusable path, repository, parsing, redaction, and policy helpers.
- `src/os.rs`: the only audited libc/unsafe boundary; the rest of the crate denies unsafe code.
Avoid moving policy into generic utilities or transport details into tools. A module should own one state transition and
return typed data to the next layer.
## Validation expectations
The normal gate is:
```bash
cargo fmt --all -- --check
cargo clippy --all-targets --all-features --locked -- -D warnings
cargo test --all-features --locked
cargo test --features loom --lib loom_
cargo +1.90.0 check --all-features --locked
cargo package --locked
cargo deny --all-features check
cargo audit --deny warnings
```
Changes to parser inputs should update fuzz targets. Hot-path claims should compile and, when meaningful, update the
Criterion benchmarks. File changes need success and failure-atomicity coverage; daemon changes need multi-adapter and
cancellation races; MCP changes need schema plus HTTP integration tests.