Skip to content

Commit 7b1a415

Browse files
Restore and update AGENTS.md files for all packages
Restores AI agent guidance removed in the previous cleanup commit, with corrections and a new gov-components entry: - Root AGENTS.md: fix LWC location (react-bridge, not react-uswds), correct component counts (59 react-uswds, 83 gov-components, 135 component-lab pages), fix e2e commands to match package.json scripts - packages/react-uswds/AGENTS.md: remove stale claims that gateway/config live here (they are in react-bridge); add i18n pattern docs - packages/react-bridge/AGENTS.md: remove stale sfGpsDsUsGovConfigLoader reference (not in this package); fix createLwcCallbackGateway naming - packages/gov-components/AGENTS.md: new file — 83 gov-specific components, import rules, test commands
1 parent 5301b58 commit 7b1a415

4 files changed

Lines changed: 318 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# AGENTS.md — GPS Design Systems React Monorepo
2+
3+
This file tells AI coding agents how to work in this repository. Read it before making any changes.
4+
5+
---
6+
7+
## Repo structure
8+
9+
```
10+
packages/
11+
react-bridge/ ← Platform infrastructure (design-system-agnostic)
12+
src/
13+
gateway/ GatewayProvider, useGatewayQuery/Mutation, MockGateway,
14+
LwcCallbackGateway, GraphQLGateway, types
15+
config/ AppConfigProvider, useConfig, useFeatureFlag
16+
wizard/ createWizardStore (Zustand), useWizardStep (RHF + Zod)
17+
pubsub/ usePlatformEvent (Platform Events / CDC)
18+
utils/ stripProxy
19+
main/default/lwc/ LWC host components: sfGpsDsReactMount, lwcReactConfigLoader,
20+
lwcConfigUtils, lwcEmpBridge
21+
22+
react-uswds/ ← USWDS design system package (@sfgps-ds/react-uswds)
23+
src/
24+
components/ 59 React components (USWDS 3.x)
25+
context/ SfGpsDsUsGovContext / Provider / useAssets
26+
hooks/ useUniqueId, useAssets, useAccStyleTokens, useI18n
27+
utils/ safeHtml, sanitizeHref, formUtils
28+
i18n/ react-i18next integration
29+
locales/ en.json and other locale bundles
30+
main/default/
31+
classes/ Apex controllers: AppConfigController, CaseController,
32+
ContactController, ApexFrontdoorService
33+
networks/ Experience Cloud network metadata
34+
35+
gov-components/ ← Government-specific components (@gov-ido/gov-components)
36+
src/
37+
components/ 83 gov-specific React components
38+
39+
docs/ All documentation — platform guides and design-system guides
40+
e2e/ Playwright tests against local Vite fixture (no org required)
41+
showcase/ UIBundle showcase app for Salesforce React Experience Cloud
42+
apps/component-lab/ Local dev fixture — 135 component pages
43+
examples/ Reference app implementations for PS teams
44+
scripts/ Utility scripts
45+
```
46+
47+
**Package boundary rule:** `react-bridge` has zero imports from `react-uswds` or `gov-components`. It is platform infrastructure only. If code requires a design-system import, it belongs in that design-system package, not here.
48+
49+
---
50+
51+
## Commands
52+
53+
### Install
54+
```bash
55+
npm ci # install from lockfile
56+
```
57+
58+
### Build
59+
```bash
60+
npm run build # build all packages (tsup for react-uswds)
61+
npm run build --workspace=packages/react-uswds
62+
```
63+
64+
### Test
65+
```bash
66+
npm run test --workspaces --if-present # all packages
67+
npm run test --workspace=packages/react-uswds # USWDS unit tests
68+
npm run test --workspace=packages/react-bridge # bridge unit tests
69+
npm run test --workspace=packages/gov-components # gov-components unit tests
70+
npm run test:coverage --workspace=packages/react-uswds # with coverage thresholds
71+
```
72+
73+
### Lint & format
74+
```bash
75+
npm run lint # ESLint across all packages
76+
npm run format:check # Prettier dry-run
77+
npm run format # Prettier write
78+
```
79+
80+
### E2E (local fixture — no org required)
81+
```bash
82+
npm run e2e # run against local Vite fixture
83+
npm run e2e:ui # Playwright interactive UI
84+
npm run dev # start local Vite dev server (port 4173)
85+
```
86+
87+
### Deploy to Salesforce
88+
```bash
89+
sf project deploy start --source-dir showcase/main/default/uiBundles --target-org BUILD
90+
sf org open --target-org BUILD
91+
```
92+
93+
---
94+
95+
## Key architectural constraints
96+
97+
### React component rules
98+
- React components never import `@salesforce/` modules — those are resolved at LWC compile time and are unavailable in React context.
99+
- All form components are controlled inputs: the parent owns the value and passes it via props.
100+
- Use `useId()` for stable, SSR-safe unique IDs. It is deterministic, Concurrent-Mode-safe, and collision-free.
101+
102+
### Security rules
103+
- Never call `DOMPurify(window)` at module evaluation time. Initialize lazily on first use.
104+
- Never use `document.activeElement` for focus management in components. Pass trigger elements explicitly.
105+
- Never use `relatedTarget.closest(...)` in blur handlers. Use a boolean ref flag instead.
106+
- Use `queueMicrotask`, not `setTimeout`, for post-render focus moves.
107+
108+
### Import rules
109+
- Import `@salesforce/agentforce-conversation-client` only from `src/agentforce.ts`, never from `src/index.ts`
110+
- Never import `@salesforce/apex/*`, `@salesforce/label/*`, `@salesforce/schema/*`, `@salesforce/user/*`, `@salesforce/client/*`, `@salesforce/featureFlag/*`, `@salesforce/customPermission/*`, `@salesforce/resourceUrl/*`, or `@salesforce/messageChannel/*` in any React source file — these are LWC compile-time virtual modules and cannot be resolved by npm or any bundler. ESLint rule SB-1 enforces this as a build error.
111+
- Never import `@sfgps-ds/react-uswds` or `@gov-ido/gov-components` from any file in `packages/react-bridge/`. ESLint rule PB-1 enforces this.
112+
- Never use `JSON.parse(JSON.stringify(` in `packages/react-bridge/`. Use `stripProxy()` instead. ESLint rule SER-2 enforces this.
113+
114+
---
115+
116+
## What to do / what not to do
117+
118+
### Do
119+
- Add new USWDS React components under `packages/react-uswds/src/components/<ComponentName>/`
120+
- Export new public components from `packages/react-uswds/src/index.ts`
121+
- Export Agentforce-specific components from `packages/react-uswds/src/agentforce.ts` (not the main barrel)
122+
- Add platform-agnostic infrastructure (gateways, stores, hooks) to `packages/react-bridge/src/`
123+
- Add gov-specific components under `packages/gov-components/src/components/<ComponentName>/`
124+
- Write Vitest tests alongside source in `src/**/__tests__/`
125+
- Write E2E tests in `e2e/tests/`
126+
127+
### Do not
128+
- Import `@salesforce/agentforce-conversation-client` from the main barrel (`index.ts`) — it belongs in `agentforce.ts` only
129+
- Import `@salesforce/` LWC virtual modules (`@salesforce/apex/*`, `@salesforce/label/*`, etc.) in any React file — LWC compile-time modules; use the gateway abstraction instead (ESLint SB-1)
130+
- Import `@sfgps-ds/react-uswds` or `@gov-ido/gov-components` from `packages/react-bridge/` — bridge is design-system-agnostic (ESLint PB-1)
131+
- Use `JSON.parse(JSON.stringify(` for LWC→React data crossing — use `stripProxy()` instead (ESLint SER-2)
132+
- Call `DOMPurify(window)` at module evaluation time
133+
- Use `document.activeElement` for focus management in React components
134+
- Add `dompurify` back to `dependencies` in `react-uswds` — it is a `peerDependency` and externalized from the tsup bundle
135+
136+
---
137+
138+
## Known pre-existing issues
139+
- `safeHtml.test.ts` — all tests pass. If test `[SEC-1D]` regresses, the fix is the `afterSanitizeAttributes` hook in `getPurifier()` inside `src/utils/safeHtml.ts`.
140+
141+
---
142+
143+
## Testing philosophy
144+
- Unit tests live in `src/**/__tests__/` and must run in Node/jsdom without a Salesforce org.
145+
- E2E tests in `e2e/` run against a local Vite fixture. They do not require a Salesforce org.
146+
- Coverage thresholds are enforced at 80% for `react-uswds`. Do not lower them.

packages/gov-components/AGENTS.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# AGENTS.md — packages/gov-components
2+
3+
See the [root AGENTS.md](../../AGENTS.md) for full context. This file adds gov-components-specific rules.
4+
5+
## What this package is
6+
7+
Government-specific React components that complement the USWDS primitives in `react-uswds`. Published as `@gov-ido/gov-components`. Contains 83 components organized around government service patterns (case management, civic navigation, program enrollment, accessibility panels, etc.).
8+
9+
Same structure as `react-uswds`:
10+
- `src/components/` — 83 government-specific components
11+
- `src/context/``GovI18nProvider`
12+
- `src/hooks/` — shared hooks
13+
- `src/i18n/` — react-i18next integration
14+
- `src/locales/` — locale bundles
15+
- `src/utils/` — shared utilities
16+
- `src/index.ts` — barrel export
17+
18+
## Test commands
19+
20+
```bash
21+
npm run test --workspace=packages/gov-components # unit tests
22+
npm run test:coverage --workspace=packages/gov-components
23+
npm run build --workspace=packages/gov-components
24+
```
25+
26+
## Adding a new component
27+
28+
1. Create `src/components/<ComponentName>/<ComponentName>.tsx` and `index.ts`
29+
2. Export from `src/index.ts`
30+
3. Add tests to `src/components/<ComponentName>/__tests__/<ComponentName>.test.tsx` — include a vitest-axe accessibility assertion
31+
4. Add a SPEC.md in the component directory documenting the public prop contract
32+
33+
## Import rules
34+
35+
- May import from `@sfgps-ds/react-uswds` for USWDS primitives (Banner, Button, Alert, etc.)
36+
- Must NOT import from `packages/react-bridge/` — no gateway or config coupling in this package
37+
- Follow the same `safeHtml` / `sanitizeHref` security rules as `react-uswds`

packages/react-bridge/AGENTS.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# AGENTS.md — packages/react-bridge
2+
3+
See the [root AGENTS.md](../../AGENTS.md) for full context. This file adds bridge-specific rules.
4+
5+
## What this package is
6+
7+
Platform infrastructure. Design-system-agnostic. Exports:
8+
9+
**Gateway layer** — abstracts how React talks to the Salesforce backend:
10+
- `GatewayProvider` / `useGateway` — injects a Gateway instance into the component tree
11+
- `useGatewayQuery(key, resource, params?)` — TanStack Query wrapper for `gateway.get()`; re-fetches when `key` changes
12+
- `useGatewayMutation(resource, method?, idField?)` — TanStack Query wrapper for post/patch/delete; includes in-flight double-submit guard
13+
- `MockGateway` — in-memory implementation for tests and local dev; `.on('resource.method', handler)` fluent API
14+
- `createLwcCallbackGateway(dispatch)` — production impl that routes calls to Apex `@AuraEnabled` methods via the LWC host
15+
- `createGraphQLGateway(routes)` — production impl backed by `@salesforce/sdk-data` GraphQL API
16+
- `relayEdges(connection)` / `sfValue(field)` — helpers for unwrapping Salesforce GraphQL response shapes
17+
18+
**Config layer** — typed org/user context from Apex Custom Metadata:
19+
- `AppConfigProvider` — Zod-validates the config object from the LWC host; safe defaults in production
20+
- `useConfig()` — returns `{ orgId, userId, userLocale, isProduction, featureFlags, agentforceAgentId }`
21+
- `useFeatureFlag(flag)` — returns `boolean`; defaults to `false` for absent or undefined flags
22+
23+
**Wizard layer** — multi-step form state (Zustand + React Hook Form + Zod):
24+
- `createWizardStore<T>(initialPayload)` — factory that creates a typed Zustand store; call at module scope, never inside a component
25+
- `useWizardStep({ schema, storePayload, updatePayload, onNext })` — wires RHF + zodResolver to one step; reads `defaultValues` from store (preserves back-navigation); only calls `updatePayload` + `onNext` when Zod passes
26+
27+
**Pub/Sub layer** — Platform Events and Change Data Capture:
28+
- `usePlatformEvent<T>({ envelope, channel, onEvent })` — reads the `__emp__` prop pushed by `lwcEmpBridge` via `_setReactProps`; deduplicates by `replayId`; ignores envelopes from other channels
29+
- `EmpEnvelope<T>` — type matching the `lightning/empApi` subscribe() message shape
30+
31+
**LWC host components** (`main/default/lwc/`):
32+
- `sfGpsDsReactMount` — owns the `lwc:dom="manual"` div; mounts React via `ReactDOM.createRoot`
33+
- `lwcReactConfigLoader` — abstract base class; coordinates `loadScript`/`@wire(getConfig)` with React lifecycle; provides `_dispatch()` and `_setReactProps()` to subclasses
34+
- `lwcConfigUtils` — pure JS utilities: `stripProxy`, `normalizeApexError`, `createFrontdoorScheduler`
35+
- `lwcEmpBridge` — utility class (not a visual component); subscribes to `lightning/empApi` channels and forwards raw envelopes to React via `_setReactProps({ __emp__: envelope })`
36+
37+
**Utils:**
38+
- `stripProxy(value)` — serialize `@salesforce/sdk-data` proxy membranes; required for any data crossing the LWC→React boundary
39+
40+
## Hard rules
41+
42+
**This package must have zero imports from `@sfgps-ds/react-uswds`, `@gov-ido/gov-components`, or any design-system package.**
43+
44+
Choosing where to add new code:
45+
- Pure platform logic with no React/LWC imports → `lwcConfigUtils.js`
46+
- React hook or context that is design-system-agnostic → `packages/react-bridge/src/`
47+
- New abstract getter for design-system subclasses → abstract getter on `lwcReactConfigLoader` that throws when not implemented
48+
- USWDS-specific component or hook → `packages/react-uswds/`
49+
- Gov-specific component → `packages/gov-components/`
50+
51+
## Test commands
52+
53+
```bash
54+
npm run test --workspace=packages/react-bridge # runs src/**/__tests__/**
55+
npm run test:watch --workspace=packages/react-bridge
56+
```
57+
58+
Tests run in jsdom. `lightning/empApi` is aliased to `__mocks__/empApiMock.js`. `@salesforce/sdk-data` is aliased to `__mocks__/sdkDataMock.js`. LWC class code cannot be imported — use the harness pattern in `bridgeLifecycle.test.ts`.
59+
60+
## Key invariants
61+
62+
**`queryKey` must include all params that affect the response.** `useGatewayQuery(['contacts', accountId], 'contacts', { accountId })` — if `accountId` changes but the key stays `['contacts']`, TanStack will not refetch. The key controls cache identity; the params are forwarded to `gateway.get()` for the fetch itself.
63+
64+
**`createWizardStore` at module scope, not inside a component.** A store created inside a component is recreated on every render, losing all accumulated payload. Create it once at the top level of the file that exports it.
65+
66+
**`usePlatformEvent` channel must exactly match the empApi channel string.** `/event/CaseUpdate__e` and `/data/CaseChangeEvent` are different channels. The hook silently ignores envelopes whose `envelope.channel` does not match the `channel` option — this is intentional (a single `__emp__` prop carries all subscriptions).
67+
68+
**`stripProxy` is the only safe serializer** for data crossing the LWC→React boundary. `JSON.parse(JSON.stringify())` silently destroys `Map`/`Set`, converts `BigInt` to a throw, and mangles `Date`. See `lwcConfigUtils.js` and `src/utils/stripProxy.ts`.
69+
70+
## Adding a new abstract getter
71+
72+
If you need `lwcReactConfigLoader` to accept a new design-system-specific value (e.g. a second stylesheet path), add an abstract getter that throws when not implemented:
73+
74+
```js
75+
// lwcReactConfigLoader.js
76+
get _myNewGetter() {
77+
throw new Error('lwcReactConfigLoader: _myNewGetter not implemented — override in subclass');
78+
}
79+
```
80+
81+
Forgetting this step causes a runtime throw before React mounts.

packages/react-uswds/AGENTS.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# AGENTS.md — packages/react-uswds
2+
3+
See the [root AGENTS.md](../../AGENTS.md) for full context. This file adds USWDS-package-specific rules.
4+
5+
## What this package is
6+
7+
The USWDS 3.x design system implementation. Contains:
8+
- React components (`src/components/`) — 59 USWDS UI primitives
9+
- Context, hooks, and i18n (`src/context/`, `src/hooks/`, `src/i18n/`, `src/locales/`)
10+
- Security utilities (`src/utils/`) — `safeHtml`, `sanitizeHref`, `formUtils`
11+
- Apex controllers (`main/default/classes/`) — `AppConfigController`, `CaseController`, `ContactController`, `ApexFrontdoorService`
12+
- Network metadata (`main/default/networks/`)
13+
14+
**Gateway, config, wizard, and pubsub infrastructure live in `packages/react-bridge/`, not here.**
15+
16+
## Test commands
17+
18+
```bash
19+
npm run test --workspace=packages/react-uswds # unit tests (src/**/__tests__/)
20+
npm run test:coverage --workspace=packages/react-uswds # must maintain 80% thresholds
21+
npm run build --workspace=packages/react-uswds # tsup build -> dist/
22+
```
23+
24+
## Adding a new component
25+
26+
1. Create `src/components/<ComponentName>/<ComponentName>.tsx` and `index.ts`
27+
2. Export from `src/index.ts` (main barrel) — or `src/agentforce.ts` if Salesforce-product-specific
28+
3. Add tests to `src/components/<ComponentName>/__tests__/<ComponentName>.test.tsx`
29+
4. Add a SPEC.md in the component directory documenting the public contract
30+
5. Add an E2E fixture page in `e2e/fixtures/src/pages/<ComponentName>Page.tsx` and route it in `e2e/fixtures/src/main.tsx`
31+
6. Add an E2E spec in `e2e/tests/<componentname>.spec.ts`
32+
33+
## Barrel export rules
34+
35+
- **`src/index.ts`** — USWDS components, hooks, context, form utilities, sanitization. No `@salesforce/agentforce-conversation-client` imports.
36+
- **`src/agentforce.ts`**`AgentforceWidget` only. Importing from this sub-path (`@sfgps-ds/react-uswds/agentforce`) requires `@salesforce/agentforce-conversation-client` as a peer.
37+
38+
Never re-add `AgentforceWidget` to the main barrel. It pulls in the ACC SDK for every consumer regardless of whether they use Agentforce.
39+
40+
## Security constraints
41+
42+
- All rich HTML from LWC props must go through `safeHtml()` before rendering as raw markup.
43+
- All `<a href>` values must go through `sanitizeHref()`.
44+
- The `afterSanitizeAttributes` hook in `safeHtml.ts` removes `data:` URIs — do not remove or restructure `getPurifier()` without running the full `safeHtml.test.ts` suite, including the SEC-1D test.
45+
46+
## dompurify is a peer dependency
47+
48+
Do not add `dompurify` back to `dependencies`. It is a `peerDependency` and is externalized in `tsup.config.ts`. Consumers that use rich HTML components must install it themselves.
49+
50+
## i18n pattern
51+
52+
Replace hardcoded UI strings (aria-labels, validation messages, button text) with `t('namespace.key')` using the `useI18n()` hook. Add English strings to `src/locales/en.json`. Use the prop-override pattern: `propValue ?? t('key')` so callers can always pass their own string.
53+
54+
Every component with i18n strings needs an `__tests__/<ComponentName>.i18n.test.tsx` that verifies translated strings render and prop overrides win.

0 commit comments

Comments
 (0)