5.4 KiB
Agent Guidelines for dashboard/
These instructions apply only to the dashboard/ subtree of XControl. They augment the root-level AGENTS.md with stricter rules specifically for the Next.js UI codebase.
The dashboard is a Next.js App Router application implemented in TypeScript with Tailwind CSS, Zustand state management, and Vitest/Playwright tests.
This document defines the architectural rules that all contributors — human or AI agents — must follow when modifying any code under dashboard/.
📌 1. State Management Rules (Zustand-Only Architecture)
Global state inconsistencies are the primary source of UI bugs and unpredictability. To eliminate this entire class of issues, the dashboard enforces:
✅ Zustand is the ONLY allowed global/shared state mechanism. ❌ React Context Providers are disallowed for global state.
This includes:
No createContext, useContext, or <Context.Provider> for app-level data (auth/session/user/theme/language/insight/workbench/shared config). No “hybrid” patterns where Zustand data is mirrored inside a Provider. No component-level useState / useEffect holding cross-component state.
✔ All shared state MUST live inside Zustand slices
Each slice must: Export a useXStore(selector) function. Expose clear state + actions. Remain serializable for hydration when needed. Keep the shape stable and predictable.
✔ Recommended slice structure (pattern) /dashboard/src/state/ user.ts → auth/session theme.ts → light/dark/system language.ts → i18n insight.ts → insight editor / workbench runtime.ts → runtime service config (hydrated from YAML)
Slices should follow this format:
export const useUserStore = create()( persist( (set, get) => ({ user: null, isLoading: false, setUser: (u) => set({ user: u }), clearUser: () => set({ user: null }), }), { name: 'user' } ) )
📌 2. URL-Synchronized State Must Live in Zustand
Features such as:
insight editor / workbench state encoded shareable links URL → state hydration state → URL serialization MUST be handled inside the Zustand slice not in the component tree.
❌ Forbidden
Components containing URL parsing logic Components reading searchParams and storing them in local state Effects that attempt to “mirror” global data into component-local state ✔ Mandatory
Zustand slices must expose helpers such as:
hydrateFromURL(searchParams: URLSearchParams) syncToURL(router: AppRouterInstance) serialize(): string
This keeps the UI stateless and predictable.
📌 3. Component-level State Rules
Local UI state (modal open, hover, controlled inputs) is allowed:
Allowed:
useState for purely local visuals useEffect for browser-only side effects useRef for DOM details
Not allowed:
useState for data needed across pages/components useEffect that propagates shared state upward
When unsure: If two components could ever read it → it belongs in Zustand.
📌 4. File Structure & Code Conventions Directory structure
Maintain component, state, and utility layout:
src/ app/ → routes (App Router) components/ → presentational components state/ → Zustand slices hooks/ → reusable UI hooks lib/ → shared utilities (non-state) config/ → runtime-service-config.yaml and loaders
Code style
ESLint + Prettier formatting
2-space indentation
Single quotes
No unused exports
No default exports for slices or large utilities (Easier for static analysis + tree shaking)
📌 5. Environment, Config & Runtime Rules Declarative configuration only
Do not add browser-only environment variables.
All new runtime config fields must go into:
dashboard/config/runtime-service-config.yaml
And be hydrated by a Zustand slice (e.g., runtime.ts).
📌 6. AI Agent (Codex/GPT) Rules — Strict Mode
Because the dashboard often uses AI to refactor/upgrade code, the following constraints apply specifically to code generated by agents:
🚫 Agents MUST NOT:
Generate any form of React Provider for global state
Introduce hybrid Context+Zustand patterns
Use component-level state for shared logic
Use browser APIs (window, localStorage) in server-compatible modules
Change directory structure without explicit instruction
Generate environment variables not reflected in runtime-service-config.yaml
✅ Agents MUST:
Implement all shared logic as Zustand slices
Keep slices serializable and deterministic
Produce code compatible with Next.js App Router (SSR + CSR safe)
Follow ESLint and existing style conventions
Ensure newly generated slices include proper actions/selectors
Prefer pure functions and stable keys for Zustand persist middlewares
📌 7. Testing Requirements
Contributors must run:
yarn --cwd dashboard lint yarn --cwd dashboard test yarn --cwd dashboard test:e2e
Slices that handle URL hydration must include unit tests verifying:
URL → state correctness
state → URL correctness
shareable link determinism
Insight-related state should always include at least minimal test coverage.
📌 8. Summary of Key Constraints (TL;DR) 🚫 Forbidden
React Context for shared/global state
useState/useEffect for cross-component data
Ad-hoc URL parsing inside components
✔ Required Zustand-only global state URL hydration inside Zustand slices Declarative runtime config (YAML → slice) AI agents must follow deterministic slice architecture