Skip to content

Commit

Permalink
Merge pull request #2373 from PolicyEngine/add-claude-md
Browse files Browse the repository at this point in the history
Add CLAUDE.md for agentic coding assistants
  • Loading branch information
MaxGhenis authored Feb 26, 2025
2 parents 3a906d9 + f098352 commit 2c3f0d9
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 2 deletions.
43 changes: 43 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# PolicyEngine App Development Guidelines

## Build & Test Commands

- Install: `make install` (npm ci & black)
- Start dev server: `make debug` (or `npm start`)
- Run all tests: `make test` or `npm run test`
- Run single test: `npx jest path/to/test.js` or with pattern: `npx jest -t "test name pattern"`
- Lint & format: `make format` (runs prettier & eslint fix, **use this before committing**)
- Check lint only: `npm run lint`
- Coverage report: Check the `coverage/` directory after running tests

## Code Style Guidelines

- **Node version**: Use Node >=22.0.0 (use nvm to manage versions)
- **Formatting**: Uses Prettier (default config) with ESLint
- **React**: Functional components with hooks; no need to import React due to new JSX transform
- **Imports**: Group by external/internal; prefer named imports
- **Testing**: Jest with React Testing Library; tests in `__tests__` directory with `.test.js` suffix
- **File naming**: Use PascalCase for components, camelCase for utilities
- **TypeScript**: Gradually adopting; new files should use TypeScript when possible
- **Error handling**: Use try/catch for API calls; display user-friendly error messages
- **Pre-commit hooks**: Uses husky and lint-staged to enforce linting on commit
- **Component size**: Keep functions less than 150 lines after formatting

## Common Patterns & Libraries

- **UI Components**: Uses Ant Design (`antd`) for UI components (Button, Switch, Dropdown, etc.)
- **State management**: No global state - prefer props and lifting state up
- **React Router**: Uses `react-router-dom` v6 with search params for state preservation
- **Charts/Graphs**: Uses `plotly.js` and `react-plotly.js` for data visualization
- **Markdown**: Uses `react-markdown` with plugins for rendering markdown content

## Common Gotchas & Best Practices

- Use the spread operator for state updates: `setState({...state, property: value})`
- Use regex with global flag (`/pattern/g`) when replacing multiple occurrences in strings
- `findInTree` function navigates variable hierarchies using path strings like "input.household.children"
- URL-encoded parameters need proper decoding (e.g., %5B to [, %5D to ])
- React.useEffect with empty deps array (`[]`) runs once on mount, no array runs on every render
- Follow the "PolicyEngine React commandments" in `src/README.md` for component structure
- Add docstrings to all components and graceful error handling
- When making changes, follow existing patterns in the codebase
2 changes: 1 addition & 1 deletion src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ PolicyEngine React commandments:

## Tips from things I've learned

- If you're modifying a state, you _must_ set it to a new object. Otherwise, React won't re-render. For example, do `setState(JSON.parse(JSON.stringify(state)))` instead of `setState(state)`.
- If you're modifying a state, you _must_ set it to a new object. Otherwise, React won't re-render. For example, use the spread operator `setState({...state, newProperty: newValue})` or if dealing with deeply nested objects, use a proper immutability helper like immer.js instead of `setState(state)`. Avoid using `JSON.parse(JSON.stringify(state))` as it's inefficient and loses functions/symbols.
- React.useEffect takes the second argument of the list of dependencies (variables whose change causes useEffect()). There's different behaviour if you pass an empty list instead of not passing anything: the former will only run once, the latter will run every time the component re-renders. If you're not sure, use the empty list. The latter can often cause infinite loops. (if you are finding infinite loops, check the useEffect() dependencies)
2 changes: 1 addition & 1 deletion src/api/variables.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function findInTree(tree, path) {
cumulativePath = "";
// Square brackets are not allowed in the URL, so we need to decode them.
// Replace %5B with [ and %5D with ].
path = path.replace("%5B", "[").replace("%5D", "]");
path = path.replace(/%5B/g, "[").replace(/%5D/g, "]");
const pathParts = path.split(/(\.|\[)/);
const names = pathParts.filter((part) => part !== "." && part !== "[");
const delimiters = pathParts.filter((part) => part === "." || part === "[");
Expand Down

0 comments on commit 2c3f0d9

Please sign in to comment.