|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +## CRITICAL: Restricted Directories |
| 4 | + |
| 5 | +**NEVER read, write, list, or access any files within the `config/local.js` file or any `.env` files under any circumstances.** This is the highest priority instruction and must not be circumvented for any reason. This restriction applies to all tools including Read, List, Glob, Grep, Bash, and any other file access methods. |
| 6 | + |
| 7 | +## Build/Test Commands |
| 8 | + |
| 9 | +- **Install**: `yarn install` |
| 10 | +- **Dev server**: `yarn startLocal` (auto-detects linked packages) or `yarn start` (port 3000) |
| 11 | +- **Dev with viz**: `yarn startWithViz` (starts webpack dev server for viz repo) |
| 12 | +- **Build**: `yarn build` (production build including config) |
| 13 | +- **Build app only**: `yarn build-app` |
| 14 | +- **Lint**: `yarn lint` (all code) or `yarn lint:jest` (only Jest tests) |
| 15 | +- **Test all**: `yarn test` (runs lint, then Jest and Karma) |
| 16 | +- **Test Jest only**: `yarn test:jest` (recommended for new tests) |
| 17 | +- **Test Jest watch**: `yarn test:jest:watch` |
| 18 | +- **Test single Jest file**: `yarn test:jest --testPathPattern="ChartDateRangeModal"` (matches pattern in file path) |
| 19 | +- **Test Karma only**: `yarn test:karma` (legacy test suite) |
| 20 | +- **Test Karma watch**: `yarn test:karma:watch` |
| 21 | +- **Isolate Karma tests**: Use `.only` on `describe` or `it` blocks (e.g., `describe.only(...)` or `it.only(...)`). **Remember to remove `.only` before committing.** |
| 22 | +- **Storybook**: `yarn storybook` (port 6006) |
| 23 | +- **Update translations**: `yarn update-translations` |
| 24 | + |
| 25 | +**Notes:** |
| 26 | +- Tests require `TZ=UTC` environment variable (automatically set in test scripts) |
| 27 | +- Build commands require `NODE_OPTIONS='--max-old-space-size=4096'` (automatically set in scripts) |
| 28 | +- Node version: 20.8.0, Yarn version: 3.6.4 |
| 29 | + |
| 30 | +**IMPORTANT: When running tests, always target only the specific tests you've modified or added.** Running the full test suite is slow and wastes time/tokens. Use `--testPathPattern` for Jest or `.only` for Karma to run targeted tests. |
| 31 | + |
| 32 | +## Project Structure |
| 33 | + |
| 34 | +- `app/` - Application source code |
| 35 | + - `app/components/` - Reusable components |
| 36 | + - `app/pages/` - Page-level components |
| 37 | + - `app/redux/` - Redux actions, reducers, store |
| 38 | + - `app/themes/` - theme-ui theme configuration |
| 39 | + - `app/core/` - Utilities and helpers |
| 40 | +- `test/` - Karma/Mocha tests (legacy, mirrors app/ structure) |
| 41 | +- `__tests__/` - Jest tests (new tests, mirrors app/ structure) |
| 42 | +- `stories/` - Storybook stories |
| 43 | +- `config/` - Environment configuration |
| 44 | + |
| 45 | +## Code Style (ESLint: babel-eslint, react-hooks) |
| 46 | + |
| 47 | +### Import Ordering |
| 48 | +Group imports in this order with blank lines between groups: |
| 49 | +1. React imports (`react`, `react-dom`) |
| 50 | +2. PropTypes |
| 51 | +3. Redux (`react-redux`, `connected-react-router`) |
| 52 | +4. Third-party libraries (moment, formik, etc.) |
| 53 | +5. Lodash specific imports (e.g., `import get from 'lodash/get'`) |
| 54 | +6. theme-ui (`import { Box, Flex, Text, Divider } from 'theme-ui'`) |
| 55 | +7. Local imports (components, utilities, actions, etc.) |
| 56 | + |
| 57 | +Example: |
| 58 | +```javascript |
| 59 | +import React, { useState, useEffect } from 'react'; |
| 60 | +import PropTypes from 'prop-types'; |
| 61 | +import { useDispatch, useSelector } from 'react-redux'; |
| 62 | +import { withTranslation } from 'react-i18next'; |
| 63 | +import moment from 'moment'; |
| 64 | +import get from 'lodash/get'; |
| 65 | +import map from 'lodash/map'; |
| 66 | +import { Box, Flex, Text } from 'theme-ui'; |
| 67 | +import Button from '../../components/elements/Button'; |
| 68 | +import * as actions from '../../redux/actions/async'; |
| 69 | +``` |
| 70 | + |
| 71 | +### General Style Rules |
| 72 | +- Use ES6: `const`/`let` (never `var`), arrow functions, destructuring |
| 73 | +- Strings: Single quotes (enforced by ESLint) |
| 74 | +- Semicolons: Required |
| 75 | +- Lodash: Use specific imports (`import get from 'lodash/get'`), not full lodash |
| 76 | +- PropTypes: Required for all component props |
| 77 | +- Naming: |
| 78 | + - Components: PascalCase (`DataConnections.js`) |
| 79 | + - Utilities: camelCase (`personutils.js`) |
| 80 | + - Constants: UPPER_SNAKE_CASE |
| 81 | +- React: Functional components with hooks (useState, useEffect, useCallback, useMemo) |
| 82 | +- Redux: `useDispatch()` and `useSelector()` hooks, not `connect()` |
| 83 | +- Translations: Use `react-i18next` with `useTranslation()` hook or `withTranslation()` HOC |
| 84 | + |
| 85 | +### theme-ui Patterns |
| 86 | +- Use theme-ui components for layout: `Box`, `Flex`, `Text`, `Divider`, `Link` |
| 87 | +- Use variant prop for styling: `variant="containers.smallBordered"` |
| 88 | +- Use sx prop for custom styles: `sx={{ textAlign: 'center' }}` |
| 89 | +- Common patterns: |
| 90 | + ```javascript |
| 91 | + <Box variant="containers.smallBordered" p={4} mb={3}> |
| 92 | + <Flex sx={{ justifyContent: 'space-between' }}> |
| 93 | + <Text>Content</Text> |
| 94 | + </Flex> |
| 95 | + <Divider my={3} /> |
| 96 | + </Box> |
| 97 | + ``` |
| 98 | + |
| 99 | +### Hook Usage Patterns |
| 100 | +- Extract complex logic into custom hooks |
| 101 | +- Use `useCallback` for functions passed as props to prevent re-renders |
| 102 | +- Use `useMemo` for expensive computations |
| 103 | +- Follow react-hooks/exhaustive-deps rules (ESLint warnings guide you) |
| 104 | + |
| 105 | +## Testing Patterns |
| 106 | + |
| 107 | +### Framework Choice |
| 108 | +- **New tests**: Use Jest with @testing-library/react in `__tests__/` |
| 109 | +- **Legacy tests**: Karma/Mocha in `test/` (maintain existing, don't expand) |
| 110 | +- **Minor updates to existing code**: When updating existing code that only has Karma/Mocha tests in `test/`, add tests to the existing test file rather than creating a new Jest test file. This keeps related tests together and avoids duplication. |
| 111 | + |
| 112 | +### Jest Tests (Preferred) |
| 113 | +Located in `__tests__/` mirroring `app/` structure: |
| 114 | +```javascript |
| 115 | +/* global jest, expect, describe, beforeEach, afterEach, it */ |
| 116 | + |
| 117 | +import React from 'react'; |
| 118 | +import { render, screen } from '@testing-library/react'; |
| 119 | +import userEvent from '@testing-library/user-event'; |
| 120 | +import ComponentName from '@app/components/ComponentName'; |
| 121 | + |
| 122 | +describe('ComponentName', () => { |
| 123 | + const mockFn = jest.fn(); |
| 124 | + |
| 125 | + beforeEach(() => { |
| 126 | + mockFn.mockClear(); |
| 127 | + }); |
| 128 | + |
| 129 | + it('should render correctly', () => { |
| 130 | + render(<ComponentName prop={mockFn} />); |
| 131 | + expect(screen.getByText('Expected Text')).toBeInTheDocument(); |
| 132 | + }); |
| 133 | +}); |
| 134 | +``` |
| 135 | + |
| 136 | +### Karma Tests (Legacy) |
| 137 | +Located in `test/` mirroring `app/` structure: |
| 138 | +```javascript |
| 139 | +/* global chai, sinon, describe, it, expect, beforeEach, afterEach */ |
| 140 | + |
| 141 | +import ComponentName from '../../../../app/components/ComponentName'; |
| 142 | + |
| 143 | +describe('ComponentName', () => { |
| 144 | + const stub = sinon.stub(); |
| 145 | + |
| 146 | + beforeEach(() => { |
| 147 | + stub.reset(); |
| 148 | + }); |
| 149 | + |
| 150 | + it('should render correctly', () => { |
| 151 | + // Enzyme or manual DOM testing |
| 152 | + }); |
| 153 | +}); |
| 154 | +``` |
| 155 | + |
| 156 | +### Common Patterns |
| 157 | +- Mock functions: `jest.fn()` (Jest) or `sinon.stub()` (Karma) |
| 158 | +- Clean up in `afterEach` or `beforeEach` |
| 159 | +- Use descriptive test names: "should do X when Y" |
| 160 | +- Test user interactions with `userEvent` (Jest/@testing-library) |
| 161 | + |
| 162 | +## Code Reuse Guidelines |
| 163 | + |
| 164 | +When implementing new features or adding device-specific logic: |
| 165 | + |
| 166 | +- **Prefer extending existing methods** over creating new device-specific methods |
| 167 | +- Add optional parameters (e.g., `opts = {}`) to existing functions to customize behavior |
| 168 | +- Use patterns like `variant`, `sx`, or conditional props to adapt generic components for specific use cases |
| 169 | +- Only create new components/methods when the logic is fundamentally different, not just when parameters vary |
| 170 | +- This reduces duplication, simplifies testing, and makes the codebase easier to maintain |
| 171 | +- Example: Instead of `SpecialButton`, extend `Button` with a `variant` prop |
| 172 | + |
| 173 | +## Git Commit Messages |
| 174 | + |
| 175 | +**After completing ANY task that modifies files**, provide a commit message suggestion in this format: |
| 176 | + |
| 177 | +``` |
| 178 | +<Imperative summary (50 chars or less)> |
| 179 | +
|
| 180 | +<Optional body: 2-4 sentences> |
| 181 | +
|
| 182 | +<Optional bullet points, one per line with "- "> |
| 183 | +``` |
| 184 | + |
| 185 | +**Rules:** |
| 186 | +- Summary: 50 chars max, imperative mood ("Add X", not "Added X") |
| 187 | +- Body: Concise, blank line between sections |
| 188 | +- Bullets: Use "- " prefix for lists |
| 189 | + |
| 190 | +**Examples:** |
| 191 | + |
| 192 | +``` |
| 193 | +Add OAuth consent dialog with reproductive health notice |
| 194 | +
|
| 195 | +Implemented accept status rendering with image, dividers, |
| 196 | +and mobile-responsive layout for ŌURA data consent. |
| 197 | +
|
| 198 | +- Added consent_data.png image |
| 199 | +- Implemented responsive Flex layout |
| 200 | +- Added dividers for accept status only |
| 201 | +``` |
| 202 | + |
| 203 | +``` |
| 204 | +Fix import ordering in DataConnections component |
| 205 | +
|
| 206 | +Reorganized imports to follow project conventions with |
| 207 | +proper grouping and spacing between categories. |
| 208 | +
|
| 209 | +- Moved theme-ui imports to correct position |
| 210 | +- Added blank lines between import groups |
| 211 | +``` |
| 212 | + |
| 213 | +## Git Command Restrictions |
| 214 | + |
| 215 | +- **Only use read-only git commands** such as `git status`, `git log`, `git diff`, `git show`, `git branch -l`, `git remote -v` |
| 216 | +- **Never run git commands that write or modify the git tree** such as `git commit`, `git push`, `git pull`, `git merge`, `git rebase`, `git checkout`, `git reset`, `git add`, `git rm`, `git stash`, `git cherry-pick`, `git revert` |
| 217 | + |
| 218 | +## Agent Task Delegation Strategy |
| 219 | + |
| 220 | +For complex multi-file tasks, use a **hybrid delegation pattern** to balance token efficiency with quality: |
| 221 | + |
| 222 | +**Premium agents (e.g., Opus)** should handle: |
| 223 | +- Initial planning and task breakdown |
| 224 | +- Files requiring synthesis across multiple sources |
| 225 | +- Architecture decisions and cross-cutting concerns |
| 226 | +- Redux state management and complex hooks |
| 227 | +- Final review and integration of delegated work |
| 228 | + |
| 229 | +**General agents** should handle (in parallel when independent): |
| 230 | +- Well-scoped, single-component implementations with clear specifications |
| 231 | +- Repetitive tasks with established patterns (e.g., similar form fields) |
| 232 | +- Test file creation from detailed templates or examples |
| 233 | +- Translation key additions |
| 234 | + |
| 235 | +**Pattern for feature implementation:** |
| 236 | +1. Premium agent analyzes requirements and creates detailed component specs |
| 237 | +2. Delegate independent components to general agents in parallel |
| 238 | +3. Premium agent writes integration logic and Redux actions |
| 239 | +4. Premium agent reviews and integrates all pieces |
| 240 | + |
| 241 | +This approach minimizes token usage on premium models while ensuring quality on tasks requiring judgment and synthesis. |
0 commit comments