chore: dedupe agent instruction files into CLAUDE.md
This commit is contained in:
@@ -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<Selection>({});
|
||||
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 });
|
||||
};
|
||||
```
|
||||
@@ -1,43 +1,5 @@
|
||||
<!-- gitnexus:start -->
|
||||
# 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` |
|
||||
|
||||
<!-- gitnexus:end -->
|
||||
@CLAUDE.md
|
||||
|
||||
Reference in New Issue
Block a user