diff --git a/AGENT.md b/AGENT.md deleted file mode 100644 index 52e8f412..00000000 --- a/AGENT.md +++ /dev/null @@ -1,265 +0,0 @@ -# React State and Request Patterns - -These guidelines define preferred patterns for request handling, state updates, and side-effect management in React applications. - -The primary goal is to keep data flow explicit, predictable, maintainable, and performant while avoiding unnecessary rerenders and effect-driven logic. - ---- - -## 1. Avoid Effect-Driven Requests - -Do not use request functions themselves as dependencies in `useEffect`. - -Avoid patterns like: - -```ts -useEffect(() => { - fetchData(); -}, [fetchData]); -``` - -Requests should be triggered explicitly by user actions or lifecycle entry points. - ---- - -## 2. Form Requests Should Be Action-Driven - -For form-related requests (such as loading `Select` options): - -- Fetch data when the form is opened for the first time. -- If later requests depend on user interactions, trigger them directly inside the interaction handler. -- Do not rely on `useEffect` dependency changes to trigger requests. - -Recommended: - -```ts -const handleOnChange = (value) => { - fetchData(value); -}; -``` - -Avoid: - -```ts -useEffect(() => { - fetchData(value); -}, [value]); -``` - -The action itself should control the request. - ---- - -## 3. Update Related States Together - -If a single action updates multiple related states: - -- Do not synchronize them through `useEffect` -- Do not derive them indirectly through `useMemo` - -Instead, update all related states directly inside the action handler. - -Recommended: - -```ts -const handleOnChange = (value) => { - setState1(...); - setState2(...); - buildState(...); -}; -``` - -Avoid implicit state synchronization chains. - ---- - -## 4. Group Strongly Related State - -If multiple states are always updated together: - -- Do not split them into multiple `useState` calls. -- Prefer a single state object. - -Recommended: - -```ts -const [state, setState] = useState({ - state1: ..., - state2: ..., - state3: ..., -}); -``` - -This reduces unnecessary rerenders and keeps state transitions predictable. - ---- - -## 5. Prefer Explicit State Flow - -Avoid chaining business logic through multiple `useEffect` hooks. - -Keep: - -- request execution -- state updates -- derived calculations - -close to the triggering action whenever possible. - -Prefer: - -```ts -const handleAction = () => { - fetchData(); - setTableData(...); - setSelectedRow(...); -}; -``` - -Over: - -```ts -useEffect(() => { - buildTable(); -}, [data]); - -useEffect(() => { - updateSelection(); -}, [tableData]); -``` - ---- - -## 6. Avoid Premature Memoization - -Do not use `useMemo` or `useCallback` unless there is a confirmed rendering or computation bottleneck. - -Overusing memoization: - -- increases complexity -- makes state flow harder to understand -- may introduce stale dependency issues - -Prefer simple and explicit logic first. - -Optimize only when necessary. - ---- - -## 7. Keep Request Logic Predictable - -A user interaction should clearly show: - -- what request is triggered -- which states are updated -- how the UI changes - -Avoid indirect update chains caused by dependency-driven effects. - -The code should make the request and update flow easy to trace. - ---- - -## 8. Prefer Action-Driven Architecture - -Prefer: - -- action-driven updates -- explicit handlers -- localized state transitions - -Over: - -- effect-driven synchronization -- cross-hook implicit updates -- reactive chains between states - -The triggering action should remain the primary source of truth for UI updates. - ---- - -# Form - -Form-specific patterns that build on the rules above. The theme: keep cascading selections (pick A → derive B → write form) on a single, predictable path. - -## 1. No Fallback for Derived Selection - -When "pick A then auto-pick B", match by rule and return `undefined` if no match — let the corresponding form field stay empty. - -Do not silently fall back to `list[0]` or another default. A fallback hides data issues and tricks the user into thinking they have a valid selection. - -```ts -const findB = (key, list) => - key ? list.find((x) => x.key === key) : undefined; -``` - -For form fields, prefer clearing with `undefined` over `''`. With Ant Design, `undefined` restores the placeholder; `''` is treated as a real value. - -## 2. Async Race Protection - -For fetches triggered by a lifecycle entry (e.g., modal open), tag each invocation with a session ref. Discard stale results if the session has rotated (the modal was closed and re-opened) by the time the response arrives. - -```ts -const sessionRef = useRef(0); - -useEffect(() => { - if (!open) { - sessionRef.current += 1; - return; - } - const session = ++sessionRef.current; - Promise.all([fetchA(), fetchB()]).then(([as, bs]) => { - if (sessionRef.current !== session) return; - applySelection(as.items[0], findB(as.items[0].key, bs.items)); - }); -}, [open]); -``` - -## 3. Reference Template - -A typical form with two cascading selectors backed by a single shared state: - -```ts -type Selection = { a?: string; b?: number }; - -const [selection, setSelection] = useState({}); -const sessionRef = useRef(0); - -const findB = (key, list) => - key ? list.find((x) => x.key === key) : undefined; - -// Single atomic write: state + form together. -const applySelection = (a, b) => { - setSelection({ a: a.name, b: b?.id }); - form.current?.setFieldsValue({ - field: b?.field, - spec: { ...currentSpec, ...b?.spec } - }); -}; - -// Trigger 1: modal opened -useEffect(() => { - if (!open) { - sessionRef.current++; - setSelection({}); - return; - } - const session = ++sessionRef.current; - Promise.all([fetchA(), fetchB()]).then(([as, bs]) => { - if (sessionRef.current !== session) return; - const first = as.items[0]; - applySelection(first, findB(first.key, bs.items)); - }); -}, [open]); - -// Trigger 2: user picks A -const handleAChange = (a) => { - applySelection(a, findB(a.key, listB)); -}; - -// Trigger 3: user picks B -const handleBChange = (b) => { - setSelection((prev) => ({ ...prev, b: b.id })); - form.current?.setFieldsValue({ ...b.fields }); -}; -``` diff --git a/AGENTS.md b/AGENTS.md index d59966df..8ad0cddb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,43 +1,5 @@ - -# GitNexus — Code Intelligence +# Agent Instructions -This project is indexed by GitNexus as **gpustack-ui** (7110 symbols, 13431 relationships, 235 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. +This project keeps a single source of truth for agent/contributor conventions in [`CLAUDE.md`](./CLAUDE.md). **Read [`CLAUDE.md`](./CLAUDE.md) and follow it.** -> Index stale? Run `node .gitnexus/run.cjs analyze` from the project root — it auto-selects an available runner. No `.gitnexus/run.cjs` yet? `npx gitnexus analyze` (npm 11 crash → `npm i -g gitnexus`; #1939). - -## Always Do - -- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user. -- **MUST run `detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows. For regression review, compare against the default branch: `detect_changes({scope: "compare", base_ref: "main"})`. -- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits. -- When exploring unfamiliar code, use `query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance. -- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `context({name: "symbolName"})`. - -## Never Do - -- NEVER edit a function, class, or method without first running `impact` on it. -- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis. -- NEVER rename symbols with find-and-replace — use `rename` which understands the call graph. -- NEVER commit changes without running `detect_changes()` to check affected scope. - -## Resources - -| Resource | Use for | -|----------|---------| -| `gitnexus://repo/gpustack-ui/context` | Codebase overview, check index freshness | -| `gitnexus://repo/gpustack-ui/clusters` | All functional areas | -| `gitnexus://repo/gpustack-ui/processes` | All execution flows | -| `gitnexus://repo/gpustack-ui/process/{name}` | Step-by-step execution trace | - -## CLI - -| Task | Read this skill file | -|------|---------------------| -| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` | -| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` | -| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` | -| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` | -| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` | -| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` | - - \ No newline at end of file +@CLAUDE.md