@clipboard-health/ai-rules 1.8.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +112 -19
  2. package/package.json +2 -5
  3. package/rules/backend/architecture.md +81 -0
  4. package/rules/backend/asyncMessaging.md +78 -0
  5. package/rules/backend/mongodb.md +59 -0
  6. package/rules/backend/notifications.md +18 -0
  7. package/rules/backend/postgres.md +5 -0
  8. package/rules/backend/restApiDesign.md +59 -0
  9. package/rules/backend/serviceTests.md +43 -0
  10. package/rules/common/configuration.md +21 -0
  11. package/rules/common/featureFlags.md +21 -0
  12. package/rules/common/gitWorkflow.md +13 -0
  13. package/rules/common/loggingObservability.md +43 -0
  14. package/rules/common/testing.md +12 -0
  15. package/rules/common/typeScript.md +86 -0
  16. package/rules/datamodeling/analytics.md +48 -0
  17. package/rules/datamodeling/castingDbtStagingModels.md +17 -0
  18. package/rules/datamodeling/dbtModelDevelopment.md +28 -0
  19. package/rules/datamodeling/dbtYamlDocumentation.md +18 -0
  20. package/rules/frontend/customHooks.md +36 -0
  21. package/rules/frontend/dataFetching.md +62 -0
  22. package/rules/frontend/e2eTesting.md +33 -0
  23. package/rules/frontend/errorHandling.md +39 -0
  24. package/rules/frontend/fileOrganization.md +25 -0
  25. package/rules/frontend/frontendTechnologyStack.md +10 -0
  26. package/rules/frontend/interactiveElements.md +9 -0
  27. package/rules/frontend/modalRoutes.md +15 -0
  28. package/rules/frontend/reactComponents.md +89 -0
  29. package/rules/frontend/styling.md +52 -0
  30. package/rules/frontend/testing.md +58 -0
  31. package/scripts/constants.js +81 -9
  32. package/scripts/execAndLog.js +21 -0
  33. package/scripts/sync.js +136 -42
  34. package/backend/AGENTS.md +0 -750
  35. package/backend/CLAUDE.md +0 -1
  36. package/common/AGENTS.md +0 -295
  37. package/common/CLAUDE.md +0 -1
  38. package/datamodeling/AGENTS.md +0 -124
  39. package/datamodeling/CLAUDE.md +0 -1
  40. package/frontend/AGENTS.md +0 -756
  41. package/frontend/CLAUDE.md +0 -1
  42. package/fullstack/AGENTS.md +0 -1211
  43. package/fullstack/CLAUDE.md +0 -1
package/README.md CHANGED
@@ -1,16 +1,17 @@
1
1
  # @clipboard-health/ai-rules
2
2
 
3
- Pre-built AI agent rules for consistent coding standards.
3
+ Pre-built AI agent rules for consistent coding standards. Uses a retrieval-based approach: generates a compressed index in `AGENTS.md` pointing to individual rule files that agents read on demand.
4
4
 
5
5
  ## Table of contents
6
6
 
7
- - [@clipboard-health/ai-rules](#clipboard-healthai-rules)
8
- - [Table of contents](#table-of-contents)
9
- - [Install](#install)
10
- - [Usage](#usage)
11
- - [Quick Start](#quick-start)
12
- - [Updating Rules](#updating-rules)
13
- - [Local development commands](#local-development-commands)
7
+ - [Install](#install)
8
+ - [Usage](#usage)
9
+ - [Quick Start](#quick-start)
10
+ - [Include/Exclude Rules](#includeexclude-rules)
11
+ - [Updating Rules](#updating-rules)
12
+ - [Available Rules](#available-rules)
13
+ - [Migration from v1](#migration-from-v1)
14
+ - [Local development commands](#local-development-commands)
14
15
 
15
16
  ## Install
16
17
 
@@ -22,7 +23,7 @@ npm install --save-dev @clipboard-health/ai-rules
22
23
 
23
24
  ### Quick Start
24
25
 
25
- 1. If you have an existing `AGENTS.md` and/or `CLAUDE.md` file in your repository, rename it to `OVERLAY.md`. The `sync-ai-rules` script you'll add below appends this file's contents to each generated file so it's loaded into LLM agent contexts.
26
+ 1. If you have an existing `AGENTS.md` and/or `CLAUDE.md` file in your repository, rename it to `OVERLAY.md`. The sync script appends this file's contents to generated `AGENTS.md` so it's loaded into LLM agent contexts.
26
27
 
27
28
  2. Choose the profile that matches your project type:
28
29
 
@@ -34,12 +35,6 @@ npm install --save-dev @clipboard-health/ai-rules
34
35
  | `fullstack` | common + frontend + backend | Monorepos, fullstack apps |
35
36
  | `datamodeling` | datamodeling | DBT data modeling |
36
37
 
37
- **Rule categories:**
38
- - **common**: TypeScript, testing, code style, error handling, key conventions
39
- - **frontend**: React patterns, hooks, performance, styling, data fetching, custom hooks
40
- - **backend**: NestJS APIs, three-tier architecture, controllers, services
41
- - **datamodeling**: data modeling, testing, yaml documentation, data cleaning, analytics
42
-
43
38
  3. Add it to your `package.json`:
44
39
 
45
40
  ```json
@@ -60,10 +55,35 @@ npm install --save-dev @clipboard-health/ai-rules
60
55
  5. Commit the generated files:
61
56
 
62
57
  ```bash
63
- git add .
58
+ git add rules/ AGENTS.md CLAUDE.md
64
59
  git commit -m "feat: add AI coding rules"
65
60
  ```
66
61
 
62
+ ### Include/Exclude Rules
63
+
64
+ Fine-tune which rules are synced using `--include` and `--exclude`:
65
+
66
+ ```bash
67
+ # Backend profile without MongoDB rules
68
+ node sync.js backend --exclude backend/mongodb
69
+
70
+ # Common profile plus one backend rule
71
+ node sync.js common --include backend/architecture
72
+
73
+ # Multiple overrides
74
+ node sync.js backend --exclude backend/mongodb backend/postgres --include frontend/testing
75
+ ```
76
+
77
+ Update your `package.json` script accordingly:
78
+
79
+ ```json
80
+ {
81
+ "scripts": {
82
+ "sync-ai-rules": "node ./node_modules/@clipboard-health/ai-rules/scripts/sync.js backend --exclude backend/mongodb"
83
+ }
84
+ }
85
+ ```
86
+
67
87
  ### Updating Rules
68
88
 
69
89
  When we release new rules or improvements:
@@ -72,17 +92,90 @@ When we release new rules or improvements:
72
92
  # Update the package
73
93
  npm update @clipboard-health/ai-rules
74
94
 
75
- # The postinstall script automatically copies the latest files
95
+ # The postinstall script automatically syncs the latest files
76
96
  npm install
77
97
 
78
98
  # Review the changes
79
- git diff .
99
+ git diff rules/ AGENTS.md
80
100
 
81
101
  # Commit the updates
82
- git add .
102
+ git add rules/ AGENTS.md CLAUDE.md
83
103
  git commit -m "chore: update AI coding rules"
84
104
  ```
85
105
 
106
+ ## Available Rules
107
+
108
+ ### common
109
+
110
+ | Rule ID | Description |
111
+ | ----------------------------- | -------------------------------------------------------------- |
112
+ | `common/configuration` | Config decisions: secrets, SSM, feature flags, DB vs hardcoded |
113
+ | `common/featureFlags` | Feature flag naming, lifecycle, and cleanup |
114
+ | `common/gitWorkflow` | Commit messages, PR titles, and pull request guidelines |
115
+ | `common/loggingObservability` | Log levels, structured context, PII avoidance |
116
+ | `common/testing` | Unit test conventions and structure |
117
+ | `common/typeScript` | TypeScript naming, types, functions, error handling |
118
+
119
+ ### backend
120
+
121
+ | Rule ID | Description |
122
+ | ------------------------ | --------------------------------------------------- |
123
+ | `backend/architecture` | Three-tier pattern: modules, services, controllers |
124
+ | `backend/asyncMessaging` | Queues, async messaging, background jobs |
125
+ | `backend/mongodb` | MongoDB/Mongoose: schemas, indexes, queries |
126
+ | `backend/notifications` | Notification and messaging patterns |
127
+ | `backend/postgres` | Postgres queries: Prisma, subqueries, feature flags |
128
+ | `backend/restApiDesign` | REST API design: JSON:API, endpoints, contracts |
129
+ | `backend/serviceTests` | Service-level integration tests |
130
+
131
+ ### frontend
132
+
133
+ | Rule ID | Description |
134
+ | ---------------------------------- | -------------------------------------------- |
135
+ | `frontend/customHooks` | React custom hooks patterns |
136
+ | `frontend/dataFetching` | React Query, API calls, caching |
137
+ | `frontend/e2eTesting` | E2E testing with Playwright |
138
+ | `frontend/errorHandling` | React error handling patterns |
139
+ | `frontend/fileOrganization` | Frontend file and folder organization |
140
+ | `frontend/frontendTechnologyStack` | Frontend library and framework choices |
141
+ | `frontend/interactiveElements` | Forms, buttons, inputs |
142
+ | `frontend/modalRoutes` | Modals and route-based dialogs |
143
+ | `frontend/reactComponents` | React component patterns, props, composition |
144
+ | `frontend/styling` | CSS, themes, responsive design |
145
+ | `frontend/testing` | React Testing Library, component tests |
146
+
147
+ ### datamodeling
148
+
149
+ | Rule ID | Description |
150
+ | -------------------------------------- | --------------------------------------- |
151
+ | `datamodeling/analytics` | Analytics data models |
152
+ | `datamodeling/castingDbtStagingModels` | Data type casting in dbt staging models |
153
+ | `datamodeling/dbtModelDevelopment` | dbt model naming, structure, testing |
154
+ | `datamodeling/dbtYamlDocumentation` | dbt YAML documentation and schema files |
155
+
156
+ ## Migration from v1
157
+
158
+ v2 replaces the monolithic `AGENTS.md` with a retrieval-based approach. Rule files are now committed to your repo under `rules/`.
159
+
160
+ 1. Update the package:
161
+
162
+ ```bash
163
+ npm install --save-dev @clipboard-health/ai-rules@latest
164
+ ```
165
+
166
+ 2. Run install to trigger sync:
167
+
168
+ ```bash
169
+ npm install
170
+ ```
171
+
172
+ 3. Add `rules/` to git and commit:
173
+
174
+ ```bash
175
+ git add rules/ AGENTS.md CLAUDE.md
176
+ git commit -m "feat!: update ai-rules to v2 retrieval-based approach"
177
+ ```
178
+
86
179
  ## Local development commands
87
180
 
88
181
  See [`package.json`](./package.json) `scripts` for a list of commands.
package/package.json CHANGED
@@ -1,11 +1,8 @@
1
1
  {
2
2
  "name": "@clipboard-health/ai-rules",
3
3
  "description": "Pre-built AI agent rules for consistent coding standards.",
4
- "version": "1.8.0",
4
+ "version": "2.0.0",
5
5
  "bugs": "https://github.com/ClipboardHealth/core-utils/issues",
6
- "devDependencies": {
7
- "@intellectronica/ruler": "0.3.31"
8
- },
9
6
  "keywords": [
10
7
  "ai",
11
8
  "best-practices",
@@ -28,6 +25,6 @@
28
25
  "url": "git+https://github.com/ClipboardHealth/core-utils.git"
29
26
  },
30
27
  "scripts": {
31
- "format": "prettier --write '.ruler/**/*.md'"
28
+ "format": "prettier --write 'rules/**/*.md'"
32
29
  }
33
30
  }
@@ -0,0 +1,81 @@
1
+ # Architecture
2
+
3
+ ## Three-Tier Architecture
4
+
5
+ All NestJS microservices follow a three-tier layered architecture:
6
+
7
+ ```text
8
+ ┌─────────────────────────────────────────────────────────────────┐
9
+ │ Entrypoints (Controllers, message consumers) │
10
+ │ - HTTP request/response, JSON:API DTO translation, auth │
11
+ ├─────────────────────────────────────────────────────────────────┤
12
+ │ Logic (NestJS services, message publishers, background jobs) │
13
+ │ - ALL business logic; works with DOs only │
14
+ │ - Knows nothing about HTTP or database specifics │
15
+ ├─────────────────────────────────────────────────────────────────┤
16
+ │ Data (Data repositories, gateways) │
17
+ │ - Database via ORM (Prisma/Mongoose), DAO ↔ DO translation │
18
+ │ - External service integrations (Gateways) │
19
+ └─────────────────────────────────────────────────────────────────┘
20
+ ```
21
+
22
+ **Module Structure:**
23
+
24
+ `ts-rest` contracts:
25
+
26
+ ```text
27
+ packages/contract-<service>/src/
28
+ ├── index.ts
29
+ └── lib
30
+ ├── constants.ts
31
+ └── contracts
32
+ ├── contract.ts
33
+ ├── health.contract.ts
34
+ ├── index.ts
35
+ └── user
36
+ ├── index.ts
37
+ ├── user.contract.ts
38
+ └── shared.ts
39
+ ```
40
+
41
+ NestJS microservice modules:
42
+
43
+ ```text
44
+ src/modules/user
45
+ ├── user.module.ts
46
+ ├── data
47
+ │ ├── user.dao.mapper.ts
48
+ │ └── user.repo.ts
49
+ ├── entrypoints
50
+ │ ├── user.controller.ts
51
+ │ ├── user.dto.mapper.ts
52
+ │ └── userCreated.consumer.ts
53
+ └── logic
54
+ ├── user.do.ts
55
+ ├── user.service.ts
56
+ ├── userCreated.service.ts
57
+ └── jobs
58
+ ├── user.job.mapper.ts
59
+ ├── userCreated.job.spec.ts
60
+ └── userCreated.job.ts
61
+ ```
62
+
63
+ **File Patterns:**
64
+
65
+ ```text
66
+ *.controller.ts - HTTP controllers (entrypoints)
67
+ *.consumer.ts - Message consumers (entrypoints)
68
+ *.service.ts - Business logic (logic)
69
+ *.job.ts - Background jobs (logic)
70
+ *.repo.ts - Database access (data)
71
+ *.gateway.ts - External services (data)
72
+ *.do.ts - Domain objects
73
+ *.dto.mapper.ts - DTO transformation
74
+ *.dao.mapper.ts - DAO transformation
75
+ ```
76
+
77
+ **Microservices Principles:**
78
+
79
+ - One domain = one module (bounded contexts)
80
+ - Specific modules know about generic, not vice versa
81
+ - Don't block Node.js thread—use background jobs for expensive operations
@@ -0,0 +1,78 @@
1
+ # Async Messaging & Background Jobs
2
+
3
+ ## When to Use
4
+
5
+ | Scenario | Solution |
6
+ | -------------------------------- | ----------------- |
7
+ | Same service producer/consumer | Background Jobs |
8
+ | Cross-service communication | EventBridge + SQS |
9
+ | Deferred work from API path | Background Jobs |
10
+ | Replacing `void` fire-and-forget | Background Jobs |
11
+ | Scaling CRON jobs | Background Jobs |
12
+
13
+ ## Background Jobs
14
+
15
+ **Creation with Transaction:**
16
+
17
+ ```typescript
18
+ async function createLicense() {
19
+ await db.transaction(async (tx) => {
20
+ const license = await tx.license.create({ data });
21
+ await jobs.enqueue(VerificationJob, { licenseId: license.id }, { transaction: tx });
22
+ });
23
+ }
24
+ ```
25
+
26
+ **Handler Pattern:**
27
+
28
+ ```typescript
29
+ class ShiftReminderJob implements Handler<ShiftReminderPayload> {
30
+ static queueName = "shift.reminder";
31
+
32
+ async perform(payload: ShiftReminderPayload, job: Job): Promise<string> {
33
+ const { shiftId } = payload;
34
+
35
+ // Fetch fresh data—don't trust stale payload
36
+ const shift = await this.shiftRepo.findById({ id: shiftId });
37
+
38
+ if (!shift || shift.isCancelled) {
39
+ return `Skipping: shift ${shiftId} not found or cancelled`;
40
+ }
41
+
42
+ try {
43
+ await this.notificationService.performSideEffect(shift);
44
+ return `Reminder sent for shift ${shiftId}`;
45
+ } catch (error) {
46
+ if (error instanceof KnownRecoverableError) throw error; // Retry
47
+ return `Skipping: ${error.message}`; // No retry
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ **Key Practices:**
54
+
55
+ - Pass minimal arguments (IDs, not objects)
56
+ - Fetch fresh data in handler
57
+ - Implement idempotency
58
+ - Check state before action
59
+ - Use Expand/Contract for job code updates
60
+
61
+ **Avoid Circular Dependencies:**
62
+
63
+ ```typescript
64
+ // Shared types file
65
+ export const NOTIFICATION_JOB = "shift-notification";
66
+ export interface NotificationJobPayload {
67
+ shiftId: string;
68
+ }
69
+
70
+ // Enqueue by string name
71
+ await jobs.enqueue<NotificationJobPayload>(NOTIFICATION_JOB, { shiftId });
72
+ ```
73
+
74
+ ## SQS/EventBridge
75
+
76
+ **Producer:** Single producer per message type, publish atomically (use jobs as outbox), deterministic message IDs, don't rely on strict ordering.
77
+
78
+ **Consumer:** Own queue per consumer, must be idempotent, separate process from API, don't auto-consume DLQs.
@@ -0,0 +1,59 @@
1
+ # MongoDB/Mongoose
2
+
3
+ **ObjectId:**
4
+
5
+ ```typescript
6
+ import mongoose, { Types, Schema } from "mongoose";
7
+
8
+ const id = new Types.ObjectId();
9
+
10
+ // In schemas
11
+ const schema = new Schema({
12
+ authorId: { type: Schema.Types.ObjectId, ref: "User" },
13
+ });
14
+
15
+ // In interfaces
16
+ interface Post {
17
+ authorId: Types.ObjectId;
18
+ }
19
+
20
+ // Validation
21
+ if (mongoose.isObjectIdOrHexString(value)) {
22
+ }
23
+ ```
24
+
25
+ **Indexes:**
26
+
27
+ - Add only when needed (slower writes tradeoff)
28
+ - Apply via code only
29
+ - Separate `indexes.ts` from `schema.ts`
30
+ - Index definitions only in owning service
31
+ - Set `autoIndex: false`
32
+ - Design covering indexes for high-traffic queries
33
+
34
+ ```text
35
+ models/User/
36
+ ├── schema.ts # Schema definition, schemaName, InferSchemaType
37
+ ├── indexes.ts # Index definitions only
38
+ ├── types.ts # Re-export types
39
+ └── index.ts # Model creation and export
40
+ ```
41
+
42
+ **Verify query plans:**
43
+
44
+ ```typescript
45
+ const explanation = await ShiftModel.find(query).explain("executionStats");
46
+ // Check: totalDocsExamined ≈ totalDocsReturned
47
+ // Good: stage 'IXSCAN'; Bad: stage 'COLLSCAN'
48
+ ```
49
+
50
+ ## Repository Pattern
51
+
52
+ ```typescript
53
+ class UserRepo {
54
+ // Named methods over generic CRUD
55
+ async findById(request: { id: UserId }): Promise<UserDo> {}
56
+ async findByEmail(request: { email: string }): Promise<UserDo> {}
57
+ async updateEmail(request: { id: UserId; email: string }): Promise<UserDo> {}
58
+ }
59
+ ```
@@ -0,0 +1,18 @@
1
+ # Notifications
2
+
3
+ Send via [Knock](https://docs.knock.app) using `@clipboard-health/notifications`.
4
+
5
+ Use `triggerChunked` to store full trigger request at job enqueue time. See package documentation for setup of `triggerNotification.job.ts`, `notificationClient.provider.ts`, and workflow keys.
6
+
7
+ **Job enqueue pattern:**
8
+
9
+ ```typescript
10
+ const jobData: SerializableTriggerChunkedRequest = {
11
+ body: { recipients: ["userId-1"], data: notificationData },
12
+ expiresAt: new Date(Date.now() + 60 * 60_000).toISOString(),
13
+ keysToRedact: ["secret"],
14
+ workflowKey: WORKFLOW_KEYS.eventStartingReminder,
15
+ };
16
+
17
+ await adapter.enqueue(TRIGGER_NOTIFICATION_JOB_NAME, jobData, { session });
18
+ ```
@@ -0,0 +1,5 @@
1
+ # Postgres
2
+
3
+ - Avoid correlated subqueries that execute per-row; they can exhaust connection pools on high-traffic endpoints
4
+ - Put significant query changes (new joins, subqueries, query rewrites) behind a feature flag for gradual rollout and instant rollback
5
+ - For complex queries (joins, aggregations, conditional filtering), prefer Prisma TypedSQL over Prisma client methods
@@ -0,0 +1,59 @@
1
+ # REST API Design
2
+
3
+ ## JSON:API Specification
4
+
5
+ Follow [JSON:API spec](https://jsonapi.org/).
6
+
7
+ ```json
8
+ {
9
+ "data": [
10
+ {
11
+ "id": "1",
12
+ "type": "shift",
13
+ "attributes": { "qualification": "nurse" },
14
+ "relationships": {
15
+ "assignedWorker": {
16
+ "data": { "type": "worker", "id": "9" }
17
+ },
18
+ "location": {
19
+ "data": { "type": "workplace", "id": "17" }
20
+ }
21
+ }
22
+ }
23
+ ]
24
+ }
25
+ ```
26
+
27
+ - Singular `type` values: `shift` not `shifts`
28
+ - Links optional (use only for pagination)
29
+ - Use `include` for related resources
30
+ - Avoid `meta` unless necessary
31
+
32
+ ## URLs
33
+
34
+ ```text
35
+ GET /urgent-shifts # lowercase kebab-case, plural nouns
36
+ GET /workers/:workerId/shifts
37
+ POST /workers/:workerId/referral-codes
38
+ ```
39
+
40
+ ## HTTP Conventions
41
+
42
+ - No PUT support — use PATCH for updates
43
+ - POST returns the created resource DTO
44
+ - 422 for unsupported filters/sorts (not 400)
45
+
46
+ ## Filtering, Sorting, Pagination
47
+
48
+ ```text
49
+ GET /shifts?filter[verified]=true&sort=startDate,-urgency&page[cursor]=abc&page[size]=50
50
+ ```
51
+
52
+ - Cursor-based pagination only (not offset)
53
+ - Avoid count totals (performance)
54
+ - Only implement filters/sorts clients need
55
+
56
+ ## Contracts
57
+
58
+ - Add contracts to `contract-<repo-name>` package
59
+ - Use `ts-rest` with composable Zod schemas
@@ -0,0 +1,43 @@
1
+ # Service Tests (Primary Testing Approach)
2
+
3
+ Test the public contract (REST endpoints, events) with real local dependencies (Postgres, Mongo, Redis). Fake slow/external services (Zendesk, Stripe).
4
+
5
+ ```typescript
6
+ describe("Documents", () => {
7
+ let tc: TestContext;
8
+
9
+ describe("GET /documents", () => {
10
+ it("returns existing documents for authenticated user", async () => {
11
+ const authToken = await tc.auth.createUser({ role: "employee" });
12
+ await tc.fixtures.createDocument({ name: "doc-1" });
13
+
14
+ const response = await tc.http.get("/documents", {
15
+ headers: { authorization: authToken },
16
+ });
17
+
18
+ expect(response.statusCode).toBe(200);
19
+ expect(response.parsedBody.data).toHaveLength(1);
20
+ });
21
+ });
22
+ });
23
+ ```
24
+
25
+ **Qualities:** One behavior per test, no shared setup, no mocking, <1 second, parallelizable.
26
+
27
+ **Testing Background Jobs:**
28
+
29
+ Don't spy on job enqueuing. Instead, run the job and assert side effects:
30
+
31
+ ```typescript
32
+ // Run the job
33
+ await tc.jobs.drainQueues("shift.reminder");
34
+
35
+ // Assert side effects
36
+ const shift = await tc.http.get(`/shifts/${shiftId}`);
37
+ expect(shift.reminderSent).toBe(true);
38
+
39
+ // Or check fakes for external calls
40
+ expect(tc.fakes.notifications.requests).toHaveLength(1);
41
+ ```
42
+
43
+ Side effects to assert: database changes, published messages, external HTTP requests.
@@ -0,0 +1,21 @@
1
+ # Configuration
2
+
3
+ ```text
4
+ Contains secrets?
5
+ └── Yes → SSM Parameter Store
6
+ └── No → Engineers-only, tolerate 1hr propagation?
7
+ └── Yes → Hardcode with @clipboard-health/config
8
+ └── No → 1:1 with DB entity OR needs custom UI?
9
+ └── Yes → Database
10
+ └── No → LaunchDarkly feature flag
11
+ ```
12
+
13
+ **NPM package management**: Use exact versions: add `save-exact=true` to `.npmrc`
14
+
15
+ ## Secrets
16
+
17
+ - `.env` locally (gitignored)
18
+ - Production: AWS SSM Parameter Store
19
+ - Prefer short-lived tokens
20
+
21
+ **Naming:** `[ENV]_[VENDOR]_[TYPE]_usedBy_[CLIENT]_[SCOPE]_[CREATED_AT]_[OWNER]`
@@ -0,0 +1,21 @@
1
+ # Feature Flags
2
+
3
+ **Naming:** `YYYY-MM-[kind]-[subject]` (e.g., `2024-03-release-new-booking-flow`)
4
+
5
+ | Kind | Purpose |
6
+ | ------------ | ------------------------ |
7
+ | `release` | Gradual rollout to 100% |
8
+ | `enable` | Kill switch |
9
+ | `experiment` | Trial for small audience |
10
+ | `configure` | Runtime config |
11
+
12
+ **Rules:**
13
+
14
+ - "Off" = default/safer value
15
+ - No permanent flags (except `configure`)
16
+ - Create archival ticket when creating flag
17
+ - Validate staging before production
18
+ - Always provide default values in code
19
+ - Clean up after full launch
20
+
21
+ **When making feature flag changes**: include LaunchDarkly link: `https://app.launchdarkly.com/projects/default/flags/{flag-key}`
@@ -0,0 +1,13 @@
1
+ # Git Workflow
2
+
3
+ Follow Conventional Commits 1.0 spec for commit messages and PR titles.
4
+
5
+ ## Pull Requests
6
+
7
+ 1. Clear title: change summary + ticket
8
+ 2. Thorough description: why, not just what
9
+ 3. Small & focused: single concept
10
+ 4. Tested: service tests + validation proof
11
+ 5. Passing CI
12
+
13
+ Link ticket, context, reasoning, and areas of concern in description.
@@ -0,0 +1,43 @@
1
+ # Logging & Observability
2
+
3
+ ## Log Levels
4
+
5
+ | Level | When |
6
+ | ----- | ------------------------------------------ |
7
+ | ERROR | Required functionality broken (2am pager?) |
8
+ | WARN | Optional broken OR recovered from failure |
9
+ | INFO | Informative, ignorable during normal ops |
10
+ | DEBUG | Local only, not production |
11
+
12
+ ## Best Practices
13
+
14
+ ```typescript
15
+ // Bad
16
+ logger.error("Operation failed");
17
+ logger.error(`Operation failed for workplace ${workplaceId}`);
18
+
19
+ // Good—structured context
20
+ logger.error("Exporting urgent shifts to CSV failed", {
21
+ workplaceId,
22
+ startDate,
23
+ endDate,
24
+ });
25
+ ```
26
+
27
+ - **Never log:** PII, PHI, tokens, secrets, SSN, account numbers, entire request/response/headers.
28
+ - Use metrics for counting:
29
+
30
+ ```typescript
31
+ datadogMetrics.increment("negotiation.errors", { state: "New York" });
32
+ ```
33
+
34
+ - Log IDs or specific fields instead of full objects:
35
+ - `workerId` (not `agent`, `hcp`, `worker`)
36
+ - `shiftId` (not `shift`)
37
+ - When multiple log statements share context, create a reusable `logContext` object:
38
+
39
+ ```typescript
40
+ const logContext = { shiftId, workerId };
41
+ logger.info("Processing shift", logContext);
42
+ logger.info("Notification sent", logContext);
43
+ ```
@@ -0,0 +1,12 @@
1
+ # Testing
2
+
3
+ ## Unit Tests
4
+
5
+ Use when: error handling hard to trigger black-box, concurrency scenarios, >5 variations, pure function logic.
6
+
7
+ ## Conventions
8
+
9
+ - `describe` for grouping
10
+ - Arrange-Act-Assert with newlines between
11
+ - Variables: `mockX`, `input`, `expected`, `actual`
12
+ - Prefer `it.each` for multiple cases