AGENTS.md@crates/nac-server/web · git:20261031.0c2bdbc · 2026-10-31 · sha256 a0ae4b078f050cbf
AGENTS.md@crates/nac-server/web git:20261031.0c2bdbcA
Immutable. This exact content never changes and is served at /api/v1/blob/a0ae4b078f050cbf.
# Web client guide This directory owns the React/Vite client, browser-facing state, checked-in API contract, component tests, and production-embedded Playwright journeys. The server remains the source of business truth and wire schemas. ## Ownership and dependencies - `src/app/features/<feature>/` owns feature model helpers, queries, controller hooks/context, and presentation when a workflow spans those concerns. - `src/app/services/api.ts` is the HTTP transport adapter. TanStack query keys, caching, cancellation, invalidation, and polling belong to query owners, not presentational components. - `packages/nac-client/` owns the runtime-free ALL-121 typed HTTP/SSE boundary. Its full resource facade and generated operation classification share the existing native methods; private control routes stay in the web adapter. The standalone service modules are compatibility facades; do not fork its transport, replay, reconnect, or command-admission logic back into the app. - `src/app/types/openapi.generated.ts` is generated from Rust/OpenAPI. Import stable aliases from `src/app/types/api.ts`; client-only refinements may live there, but do not hand-copy wire DTOs. - Keep managed-host product state inside `features/managed`. Managed orchestrator session views remain session/delegation features because they are a distinct durable topology. - Preserve navigation, abort signals, polling settlement, optimistic state, query invalidation, accessibility labels, and behavior-selection defaults. - Human product operations never invoke agent approval. A focused fresh terminal keystroke is intentional input on ordinary attach/reconnect; attaching sends no bytes. Accepted-but-uncertain delivery freezes input until explicit recovery acknowledgement and never auto-replays old input. Recovery is delivery state, not permission; the server retains denial/authentication/backend gates. - Human input uses trusted public keyboard/paste/composition/pointer events; renderer-generated replies are not intent. Bounded runtime-owned uncertainty survives view/controller replacement without storing or replaying bytes. Input activation binds source, mode, current capabilities and controller revision. Historical output preserves factual retention while suppressing all controls. Consume cached session availability for passive terminal/history views; do not trigger recovering session reads merely to render them. - `src/app/ui/` owns the pinned shadcn/Radix primitives and each presentation instance's overlay layer stack. Product code keeps importing atoms. Overlays register with `useLayer` instead of adding their own Escape, outside-press, inertness or focus-return handling, and Radix stays non-modal. The Popover atom's opt-in `focusScope` owns only initial focus and Tab navigation; see [ADR 0007](../../../docs/architecture/0007-scoped-ui-primitives.md). - Generic providers are for genuinely cross-feature browser state. Do not use them as a home for one feature's workflow. - `src/app/runtime/` owns one immutable consumer binding, private query cache, presentation stores and request/stream lifetime. Production feature owners read that context; compatibility globals are only for legacy hook fixtures. Replace the runtime when any binding changes. Keep caller routing, styles, theme and authenticated stream construction explicit at the presentation root. ## Starting points - `src/App.tsx` — provider/router composition. - `src/app/services/api.ts` — HTTP transport and error decoding. - `src/app/services/queries.ts` — stable compatibility barrel only. - `src/app/services/queries/` — focused host, direct/delegation, configuration, session, workspace, project, key, and invalidation owners. - `src/app/features/managed/` — managed model/query/controller/presentation. - `src/app/types/api.ts` — stable aliases/refinements over generated schemas. - `openapi.json`, `scripts/generate-api-types.mjs` — checked-in contract and fail-closed generator. - `e2e/` and `playwright.config.ts` — production-embedded browser coverage. ## Cohesive size exceptions - `components/inspector/ChatInputBox.tsx` keeps one composer state machine: text/selection, slash commands, attachments, send-versus-queue behavior, keyboard/IME handling, and its accessible presentation. Put server state and API mutations in query/controller owners, not in this component. - `components/modals/ConfigurationsPanel.tsx` and `SettingsModal.tsx` keep the existing dense configuration forms whose field validation and save ordering are exercised as one workflow. Managed-host panels do not belong there. - `components/modals/CreateProjectModal.tsx` keeps the coupled project location, SSH/sandbox fields and advanced launch draft in one accessible form. Ordered commands and session-request construction belong in `features/setup`, while the modal retains local field state and its presentation. - `store/runtimeStore.ts` keeps snapshot hydration and ordered live-event reduction in one store factory. Its activation fence and coupled run/tool/ transcript projection must share a lifetime; splitting the reduction across independent stores would obscure ordering and late-event protection. These existing UI owners may exceed 800 lines because splitting their tightly coupled local form/view state would scatter one workflow. Do not add unrelated queries, providers, or new product workflows to them; extract a real controller or feature boundary when new behavior proves one. ## Commands ```sh npm --prefix crates/nac-server/web run typecheck npm --prefix crates/nac-server/web run lint npm --prefix crates/nac-server/web run format:check npm --prefix crates/nac-server/web test npm --prefix crates/nac-server/web run test:client-package make test-api-contract make test-assets make test-e2e ``` ## Generated artifacts and single writers - `make generate-api-contract` writes `openapi.json`, `src/app/types/openapi.generated.ts`, and the transitive schema subset under `packages/nac-client/src/`. The generator fails on unsupported schema constructs; extend it explicitly rather than widening to `any`. - `npm ... run build` writes `../assets/dist` and `packages/nac-client/dist` and `packages/nac-presentation/dist`. Commit source and all outputs together. `vite.presentation.config.ts` reuses the native entry and build pipeline; `scripts/presentation-css.ts` scopes the generated stylesheet, and `scripts/presentation-types.mjs` removes source aliases from emitted declarations. Keep React peers external and CSS/assets opt-in; do not create a second UI or transport implementation. Do not edit hashed assets by hand. - `scripts/sync-file-icons.mjs` is the writer for synchronized icon assets when that source set changes. ## Placement mistakes - Do not encode server validation, persistence defaults, or authorization in React helpers. - Do not fetch directly from presentation components when a query owner needs to preserve cancellation/polling/invalidation semantics. - Do not scatter one managed workflow across generic modal/provider folders. - Do not edit generated API types or committed production chunks manually.