@clipboard-health/ai-rules 2.25.1 → 2.27.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 (48) hide show
  1. package/README.md +56 -39
  2. package/package.json +1 -1
  3. package/rules/backend/architecture.md +4 -0
  4. package/rules/backend/asyncMessaging.md +4 -0
  5. package/rules/backend/infrastructure.md +4 -0
  6. package/rules/backend/mongodb.md +4 -33
  7. package/rules/backend/notifications.md +4 -0
  8. package/rules/backend/postgres.md +4 -0
  9. package/rules/backend/restApiDesign.md +10 -54
  10. package/rules/backend/serviceTests.md +4 -0
  11. package/rules/common/configuration.md +4 -0
  12. package/rules/common/coreLibraries.md +4 -1
  13. package/rules/common/featureFlags.md +4 -0
  14. package/rules/common/gitWorkflow.md +4 -0
  15. package/rules/common/libraryAuthoring.md +13 -0
  16. package/rules/common/loggingObservability.md +4 -0
  17. package/rules/common/testing.md +5 -1
  18. package/rules/common/typeScript.md +12 -61
  19. package/rules/datamodeling/analytics.md +22 -15
  20. package/rules/datamodeling/castingDbtStagingModels.md +4 -0
  21. package/rules/datamodeling/dbtModelDevelopment.md +16 -12
  22. package/rules/datamodeling/dbtYamlDocumentation.md +4 -0
  23. package/rules/frontend/{fileOrganization.md → architecture.md} +22 -1
  24. package/rules/frontend/customHooks.md +4 -12
  25. package/rules/frontend/dataFetching.md +25 -16
  26. package/rules/frontend/e2eTesting.md +5 -10
  27. package/rules/frontend/reactComponents.md +48 -42
  28. package/rules/frontend/styling.md +4 -10
  29. package/rules/frontend/testing.md +6 -35
  30. package/scripts/constants.js +1 -79
  31. package/scripts/rules.js +126 -0
  32. package/scripts/sync.js +20 -61
  33. package/skills/babysit-pr/SKILL.md +4 -4
  34. package/skills/babysit-pr/scripts/_sentinel.sh +1 -1
  35. package/skills/commit-push-pr/SKILL.md +2 -2
  36. package/skills/in-depth-review/SKILL.md +491 -0
  37. package/skills/in-depth-review/references/cross-repo-evidence.md +37 -0
  38. package/skills/in-depth-review/references/posting-pr-review.md +105 -0
  39. package/skills/simple-review/SKILL.md +472 -0
  40. package/skills/simple-review/references/cross-repo-evidence.md +64 -0
  41. package/skills/simple-review/references/posting-pr-review.md +100 -0
  42. package/rules/frontend/bottomSheets.md +0 -25
  43. package/rules/frontend/businessLogicPlacement.md +0 -3
  44. package/rules/frontend/errorHandling.md +0 -39
  45. package/rules/frontend/frontendTechnologyStack.md +0 -10
  46. package/rules/frontend/interactiveElements.md +0 -19
  47. package/rules/frontend/modalRoutes.md +0 -15
  48. package/skills/flaky-test-bulk-debugger/SKILL.md +0 -89
package/README.md CHANGED
@@ -10,6 +10,7 @@ Pre-built AI agent rules for consistent coding standards. Uses a retrieval-based
10
10
  - [Include/Exclude Rules](#includeexclude-rules)
11
11
  - [Updating Rules](#updating-rules)
12
12
  - [Available Rules](#available-rules)
13
+ - [Contributing Rules](#contributing-rules)
13
14
  - [Migration from v1](#migration-from-v1)
14
15
  - [Local development commands](#local-development-commands)
15
16
 
@@ -74,6 +75,8 @@ node sync.js common --include backend/architecture
74
75
  node sync.js backend --exclude backend/mongodb backend/postgres --include frontend/testing
75
76
  ```
76
77
 
78
+ Unknown rule ids are skipped with a warning so a stale `--include`/`--exclude` never breaks installs.
79
+
77
80
  Update your `package.json` script accordingly:
78
81
 
79
82
  ```json
@@ -105,54 +108,68 @@ git commit -m "chore: update AI coding rules"
105
108
 
106
109
  ## Available Rules
107
110
 
108
- ### common
111
+ Each rule's "When to Read" text comes from the `description` field in the rule file's YAML frontmatter — the single source of truth used for the generated `AGENTS.md` index and the tables below.
109
112
 
110
- | Rule ID | Description |
111
- | ----------------------------- | -------------------------------------------------------------- |
112
- | `common/configuration` | Config decisions: secrets, SSM, feature flags, DB vs hardcoded |
113
- | `common/coreLibraries` | Available `@clipboard-health/*` shared libraries |
114
- | `common/featureFlags` | Feature flag naming, lifecycle, and cleanup |
115
- | `common/gitWorkflow` | Commit messages, PR titles, and pull request guidelines |
116
- | `common/loggingObservability` | Log levels, structured context, PII avoidance |
117
- | `common/testing` | Unit test conventions and structure |
118
- | `common/typeScript` | TypeScript naming, types, functions, error handling |
113
+ <!-- START: Auto-generated by ./scripts/populateReadme.ts -->
119
114
 
120
115
  ### backend
121
116
 
122
- | Rule ID | Description |
123
- | ------------------------ | --------------------------------------------------- |
124
- | `backend/architecture` | Three-tier pattern: modules, services, controllers |
125
- | `backend/asyncMessaging` | Queues, async messaging, background jobs |
126
- | `backend/mongodb` | MongoDB/Mongoose: schemas, indexes, queries |
127
- | `backend/notifications` | Notification and messaging patterns |
128
- | `backend/postgres` | Postgres queries: Prisma, subqueries, feature flags |
129
- | `backend/restApiDesign` | REST API design: JSON:API, endpoints, contracts |
130
- | `backend/serviceTests` | Service-level integration tests |
117
+ | Rule ID | When to Read |
118
+ | ------------------------ | ----------------------------------------------------------------------------------------- |
119
+ | `backend/architecture` | Structuring NestJS modules, services, repos: three-tier, microservices, ts-rest contracts |
120
+ | `backend/asyncMessaging` | Working with queues, async messaging, or background jobs |
121
+ | `backend/infrastructure` | Provisioning infrastructure: Terraform, Docker, ECS, DNS |
122
+ | `backend/mongodb` | Working with MongoDB/Mongoose: schemas, indexes, queries, transactions, migrations |
123
+ | `backend/notifications` | Implementing notifications via Knock: push notifications, deep links, workflow design |
124
+ | `backend/postgres` | Working with Postgres: column types, schema changes, query patterns, Prisma TypedSQL |
125
+ | `backend/restApiDesign` | Designing REST APIs: JSON:API, auth, validation, pagination, ts-rest contracts, DTOs |
126
+ | `backend/serviceTests` | Writing service tests: test data, background jobs, bug handling, migrations |
131
127
 
132
- ### frontend
128
+ ### common
133
129
 
134
- | Rule ID | Description |
135
- | ---------------------------------- | -------------------------------------------- |
136
- | `frontend/customHooks` | React custom hooks patterns |
137
- | `frontend/dataFetching` | React Query, API calls, caching |
138
- | `frontend/e2eTesting` | E2E testing with Playwright |
139
- | `frontend/errorHandling` | React error handling patterns |
140
- | `frontend/fileOrganization` | Frontend file and folder organization |
141
- | `frontend/frontendTechnologyStack` | Frontend library and framework choices |
142
- | `frontend/interactiveElements` | Forms, buttons, inputs |
143
- | `frontend/modalRoutes` | Modals and route-based dialogs |
144
- | `frontend/reactComponents` | React component patterns, props, composition |
145
- | `frontend/styling` | CSS, themes, responsive design |
146
- | `frontend/testing` | React Testing Library, component tests |
130
+ | Rule ID | When to Read |
131
+ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
132
+ | `common/configuration` | Adding config, secrets, or third-party dependencies: SSM, LaunchDarkly, DB, NPM packages |
133
+ | `common/coreLibraries` | Adding dependencies, implementing functionality, or debugging errors involving a @clipboard-health/\* library |
134
+ | `common/featureFlags` | Creating or managing feature flags: naming, lifecycle, SDK usage, Zod schemas |
135
+ | `common/gitWorkflow` | Writing commit messages, PR titles, or reviewing pull requests |
136
+ | `common/libraryAuthoring` | Authoring shared library code: @clipboard-health/\* packages or shared library modules within services (e.g., src/lib) |
137
+ | `common/loggingObservability` | Adding logging, metrics, monitoring, or observability: levels, context, PII, Datadog |
138
+ | `common/testing` | Writing unit tests: conventions, naming, structure |
139
+ | `common/typeScript` | Writing ANY TypeScript code |
147
140
 
148
141
  ### datamodeling
149
142
 
150
- | Rule ID | Description |
151
- | -------------------------------------- | --------------------------------------- |
152
- | `datamodeling/analytics` | Analytics data models |
153
- | `datamodeling/castingDbtStagingModels` | Data type casting in dbt staging models |
154
- | `datamodeling/dbtModelDevelopment` | dbt model naming, structure, testing |
155
- | `datamodeling/dbtYamlDocumentation` | dbt YAML documentation and schema files |
143
+ | Rule ID | When to Read |
144
+ | -------------------------------------- | ------------------------------------------------------------------------------ |
145
+ | `datamodeling/analytics` | Querying analytics data: dbt-mcp, Snowflake, source columns, output formatting |
146
+ | `datamodeling/castingDbtStagingModels` | Casting data types in dbt staging models |
147
+ | `datamodeling/dbtModelDevelopment` | Developing dbt models: naming, structure, testing |
148
+ | `datamodeling/dbtYamlDocumentation` | Writing dbt YAML documentation and schema files |
149
+
150
+ ### frontend
151
+
152
+ | Rule ID | When to Read |
153
+ | -------------------------- | ------------------------------------------------------------------------------------------------------------ |
154
+ | `frontend/architecture` | Frontend architecture: technology stack, file organization, where business logic lives |
155
+ | `frontend/customHooks` | Creating React custom hooks: naming, shared state with constate |
156
+ | `frontend/dataFetching` | Implementing data fetching and error handling: React Query, API calls, caching, parsedApi |
157
+ | `frontend/e2eTesting` | Writing E2E tests with Playwright |
158
+ | `frontend/reactComponents` | Building UI components: structure, composition, modals, bottom sheets, interactive elements, a11y, Storybook |
159
+ | `frontend/styling` | Styling components with MUI sx prop: theme tokens, spacing, no CSS/SCSS |
160
+ | `frontend/testing` | Writing frontend tests: React Testing Library, component tests |
161
+
162
+ <!-- END: Auto-generated by ./scripts/populateReadme.ts -->
163
+
164
+ ## Contributing Rules
165
+
166
+ A rule earns its place only if a frontier model would do the wrong thing without it: Clipboard-specific decisions, library choices, naming, and hard-won gotchas. Do not add tutorials, generic best practices, or anything a linter already enforces.
167
+
168
+ To add or change a rule:
169
+
170
+ 1. Edit or create the rule file under `rules/<category>/` with a frontmatter `description` (the "When to Read" text).
171
+ 2. Run `npx tsx scripts/populateReadme.ts` to regenerate the tables above (a test fails if they drift).
172
+ 3. New categories are directories under `rules/`; no registration needed.
156
173
 
157
174
  ## Migration from v1
158
175
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/ai-rules",
3
- "version": "2.25.1",
3
+ "version": "2.27.0",
4
4
  "description": "Pre-built AI agent rules for consistent coding standards.",
5
5
  "keywords": [
6
6
  "ai",
@@ -1,3 +1,7 @@
1
+ ---
2
+ description: "Structuring NestJS modules, services, repos: three-tier, microservices, ts-rest contracts"
3
+ ---
4
+
1
5
  # Architecture
2
6
 
3
7
  ## Three-Tier Architecture
@@ -1,3 +1,7 @@
1
+ ---
2
+ description: "Working with queues, async messaging, or background jobs"
3
+ ---
4
+
1
5
  # Async Messaging & Background Jobs
2
6
 
3
7
  ## When to Use
@@ -1,3 +1,7 @@
1
+ ---
2
+ description: "Provisioning infrastructure: Terraform, Docker, ECS, DNS"
3
+ ---
4
+
1
5
  # Infrastructure
2
6
 
3
7
  ## Terraform
@@ -1,26 +1,8 @@
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
- }
1
+ ---
2
+ description: "Working with MongoDB/Mongoose: schemas, indexes, queries, transactions, migrations"
3
+ ---
19
4
 
20
- // Validation
21
- if (mongoose.isObjectIdOrHexString(value)) {
22
- }
23
- ```
5
+ # MongoDB/Mongoose
24
6
 
25
7
  **File Structure:**
26
8
 
@@ -77,14 +59,3 @@ Run `explain("executionStats")` on new or changed queries; verify `$lookup` stag
77
59
  ## Batch Migrations
78
60
 
79
61
  Migrate large datasets in batches via background jobs using a cursor pattern (each job processes a batch and schedules the next); add 1-5s delay between batches; use the slow job group.
80
-
81
- ## Repository Pattern
82
-
83
- ```typescript
84
- class UserRepo {
85
- // Named methods over generic CRUD
86
- async findById(request: { id: UserId }): Promise<UserDo> {}
87
- async findByEmail(request: { email: string }): Promise<UserDo> {}
88
- async updateEmail(request: { id: UserId; email: string }): Promise<UserDo> {}
89
- }
90
- ```
@@ -1,3 +1,7 @@
1
+ ---
2
+ description: "Implementing notifications via Knock: push notifications, deep links, workflow design"
3
+ ---
4
+
1
5
  # Notifications
2
6
 
3
7
  Send via [Knock](https://docs.knock.app) using `@clipboard-health/notifications`. Braze is deprecated.
@@ -1,3 +1,7 @@
1
+ ---
2
+ description: "Working with Postgres: column types, schema changes, query patterns, Prisma TypedSQL"
3
+ ---
4
+
1
5
  # Postgres
2
6
 
3
7
  ## Column Types
@@ -1,38 +1,19 @@
1
+ ---
2
+ description: "Designing REST APIs: JSON:API, auth, validation, pagination, ts-rest contracts, DTOs"
3
+ ---
4
+
1
5
  # REST API Design
2
6
 
3
7
  ## JSON:API Specification
4
8
 
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
- ```
9
+ Follow the [JSON:API spec](https://jsonapi.org/) with these conventions:
26
10
 
27
11
  - Singular `type` values: `shift` not `shifts`
28
12
  - Links optional (use only for pagination)
29
13
  - Use `include` for related resources
30
14
  - Avoid `meta` unless necessary
31
15
  - Use lowerCamelCase for JSON keys
32
-
33
- ## Error Responses
34
-
35
- Return errors using JSON:API `errors` array with `code`, `status`, and `title` fields.
16
+ - Return errors using JSON:API `errors` array with `code`, `status`, and `title` fields
36
17
 
37
18
  ## URLs
38
19
 
@@ -44,27 +25,11 @@ POST /workers/:workerId/referral-codes
44
25
 
45
26
  ## HTTP Conventions
46
27
 
47
- - No PUT support — use PATCH for updates
48
- - POST returns the created resource DTO
49
- - Use only GET, POST, PATCH, DELETE
50
- - GET must be idempotent with no side effects
28
+ - Use only GET, POST, PATCH, DELETE; no PUT support — use PATCH for updates
51
29
  - Model state changes as PATCH to resource attributes (not action-specific POST endpoints)
52
-
53
- ## HTTP Status Codes
54
-
55
- | Code | Usage |
56
- | ---- | ----------------------------------------------------- |
57
- | 200 | GET, PATCH, DELETE success |
58
- | 201 | POST creation |
59
- | 202 | Accepted (async processing) |
60
- | 400 | Syntactic errors or unsupported query params |
61
- | 401 | Unauthenticated |
62
- | 403 | Forbidden |
63
- | 404 | Not found |
64
- | 409 | Conflict |
65
- | 422 | Semantic validation errors, unsupported filters/sorts |
66
- | 429 | Rate limited |
67
- | 500 | Server error |
30
+ - POST returns the created resource DTO with 201
31
+ - GET must be idempotent with no side effects
32
+ - Return 400 for syntactic errors and unsupported query params; 422 for semantic validation errors and unsupported filters/sorts
68
33
 
69
34
  ## Authentication & Authorization
70
35
 
@@ -102,15 +67,6 @@ Use helpers from `@clipboard-health/contract-core` instead of raw Zod methods in
102
67
  - Export at the DTO boundary (request/response schemas), not every intermediate schema
103
68
  - Compose relationships from shared schemas — don't redefine `type` literals per contract
104
69
 
105
- ### `parsedApi.ts` vs `api.ts`
106
-
107
- Frontend repos have two API layers:
108
-
109
- - **`api.ts`** (legacy) — does not parse responses through Zod schemas. Inferred types say `Date` for `dateTimeSchema()` fields but the runtime value is still a string. Zod transforms (`.transform()`, `dateTimeSchema()`, enum fallbacks) produce **incorrect types at runtime**.
110
- - **`parsedApi.ts`** — parses both inputs (`z.input`) and outputs (`z.output`) through schemas. Types match runtime values.
111
-
112
- Use `parsedApi.ts` for all new API calls. However, `parsedApi.ts` means invalid contract schemas will fail at runtime — ensure contracts are forwards-compatible. Do not use `parsedApi.ts` if the contract contains bare `z.enum()` values that the backend may extend, as new enum values will cause parse failures on old clients. Migrate bare `z.enum()` to `requiredEnumWithFallback`/`optionalEnumWithFallback` first.
113
-
114
70
  ## Data Transfer
115
71
 
116
72
  - Do not return database models through APIs or events; map to DTOs exposing only what clients need
@@ -1,3 +1,7 @@
1
+ ---
2
+ description: "Writing service tests: test data, background jobs, bug handling, migrations"
3
+ ---
4
+
1
5
  # Service Tests (Primary Testing Approach)
2
6
 
3
7
  Test the public contract (REST endpoints, events) with real local dependencies (Postgres, Mongo, Redis). Fake slow/external services (LaunchDarkly, Firebase, Stripe, Zendesk) and other microservices with fakes; fake the event bus.
@@ -1,3 +1,7 @@
1
+ ---
2
+ description: "Adding config, secrets, or third-party dependencies: SSM, LaunchDarkly, DB, NPM packages"
3
+ ---
4
+
1
5
  # Configuration
2
6
 
3
7
  ```text
@@ -1,3 +1,7 @@
1
+ ---
2
+ description: "Adding dependencies, implementing functionality, or debugging errors involving a @clipboard-health/* library"
3
+ ---
4
+
1
5
  # Core Libraries
2
6
 
3
7
  Clipboard's shared `@clipboard-health/*` libraries. Check here before adding new dependencies or implementing functionality from scratch. For details on each library:
@@ -34,7 +38,6 @@ When a bug traces into a `@clipboard-health/*` library, read the source code in
34
38
  - **playwright-reporter-llm**: Playwright reporter that outputs structured JSON for LLM agents. Minimal console output, flat schema, easy to filter to failures.
35
39
  - **rules-engine**: A pure functional rules engine to keep logic-dense code simple, reliable, understandable, and explainable.
36
40
  - **testing-core**: TypeScript-friendly testing utilities.
37
- - **tribunal**: Structured second opinions from advocate, skeptic, analyst, and deliberator LLM perspectives.
38
41
  - **util-ts**: TypeScript utilities.
39
42
 
40
43
  <!-- END: Auto-generated by ./populateLibraries.ts -->
@@ -1,3 +1,7 @@
1
+ ---
2
+ description: "Creating or managing feature flags: naming, lifecycle, SDK usage, Zod schemas"
3
+ ---
4
+
1
5
  # Feature Flags
2
6
 
3
7
  **Naming:** `YYYY-MM-[kind]-[subject]` (e.g., `2024-03-release-new-booking-flow`)
@@ -1,3 +1,7 @@
1
+ ---
2
+ description: "Writing commit messages, PR titles, or reviewing pull requests"
3
+ ---
4
+
1
5
  # Git Workflow
2
6
 
3
7
  - Follow Conventional Commits 1.0 spec for commit messages and PR titles.
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: "Authoring shared library code: @clipboard-health/* packages or shared library modules within services (e.g., src/lib)"
3
+ ---
4
+
5
+ # Library Authoring
6
+
7
+ Applies when writing shared library code: `@clipboard-health/*` packages and shared library modules within services (e.g., `src/lib`).
8
+
9
+ - Use object arguments and object return types in library APIs; wrap exported responses in `ServiceResult`; prefer `ServiceResult` for expected errors and reserve throwing for unexpected/unrecoverable failures
10
+ - Library API types must not contain `any` or bare `object`; prefer specific types over `unknown`, but allow `unknown` when type safety requires caller-side narrowing; use TypeScript generics
11
+ - Define the public API exclusively through index exports (`src/index.ts` in packages); place non-public code in `internal/`
12
+ - Strive for 100% test coverage in library code (`/* istanbul ignore next */` only for genuinely untestable lines)
13
+ - When wrapping another library, design the API from first principles for our use cases; do not mirror the wrapped library's API or leak implementation details through interface names
@@ -1,3 +1,7 @@
1
+ ---
2
+ description: "Adding logging, metrics, monitoring, or observability: levels, context, PII, Datadog"
3
+ ---
4
+
1
5
  # Logging & Observability
2
6
 
3
7
  ## Log Levels
@@ -1,8 +1,12 @@
1
+ ---
2
+ description: "Writing unit tests: conventions, naming, structure"
3
+ ---
4
+
1
5
  # Testing
2
6
 
3
7
  ## Unit Tests
4
8
 
5
- Use when: error handling hard to trigger black-box, concurrency scenarios, >5 variations, pure function logic.
9
+ Write unit tests for: pure function logic, error paths that are hard to trigger black-box, concurrency scenarios, and behavior with many variations (>5). Otherwise prefer testing through the public contract: service tests (backend) or component integration tests (frontend).
6
10
 
7
11
  ## Conventions
8
12
 
@@ -1,26 +1,32 @@
1
+ ---
2
+ description: "Writing ANY TypeScript code"
3
+ ---
4
+
1
5
  # TypeScript
2
6
 
7
+ ## Domain Language
8
+
9
+ - Use `worker`, not `agent`, `hcp`, or `healthcareProvider`
10
+ - Use `workplace`, not `facility`, `hcf`, or `healthcareFacility`
11
+ - Use `qualification`, not `agentRequirement`, `agentReq`, or `workerType`
12
+
3
13
  ## Naming Conventions
4
14
 
5
15
  - Avoid acronyms and abbreviations; for those widely known, use camelCase: `httpRequest`, `gpsPosition`, `cliArguments`, `apiResponse`
6
16
  - File-scoped constants: `MAX_RETRY_COUNT`
7
- - Instead of `agentRequirement`, `agentReq`, `workerType`, use `qualification`
8
- - Instead of `agent`, `hcp`, `healthcareProvider`, use `worker`
9
- - Instead of `facility`, `hcf`, `healthcareFacility`, use `workplace`
10
17
 
11
18
  ## Core Rules
12
19
 
13
- - Strict-mode TypeScript
14
20
  - Avoid type assertions (`as`, `!`) unless absolutely necessary
15
21
  - Use `function` keyword for declarations, not `const`
16
22
  - Prefer `undefined` over `null`
17
23
  - Files read top-to-bottom: exports first, internal helpers below
18
- - Boolean props: `is*`, `has*`, `should*`, `can*`
19
- - Use const assertions for constants: `as const`
20
24
  - Use immutable array methods (`toSorted`, `toReversed`) instead of mutating methods (`sort`, `reverse`)
21
25
  - Return Prisma decimal values as strings in API responses to avoid floating-point precision issues
22
26
  - Use explicit access modifiers (`public`, `private`, `protected`) on all class methods and properties
23
27
  - Use a `for` loop with `// eslint-disable-next-line no-await-in-loop` for intentional sequential execution (e.g., rate limiting, ordered processing, or resource constraints); prefer `Promise.all` when operations are independent
28
+ - Functions take a single object argument typed by an interface and destructure it inside the function body
29
+ - Make quantity values unambiguous: `{ amountInMinorUnits: 500, currencyCode: "USD" }`, `durationMinutes: 30`
24
30
 
25
31
  ## Dead Code Cleanup
26
32
 
@@ -35,37 +41,6 @@ In TypeScript code that has access to `@clipboard-health/util-ts`, prefer the na
35
41
 
36
42
  Import from `@clipboard-health/util-ts`.
37
43
 
38
- ## Types
39
-
40
- ```typescript
41
- // Quantity values—always unambiguous
42
- const money = { amountInMinorUnits: 500, currencyCode: "USD" };
43
- const durationMinutes = 30;
44
- ```
45
-
46
- ## Functions
47
-
48
- ```typescript
49
- // Object arguments with interfaces
50
- interface CreateUserRequest {
51
- email: string;
52
- name?: string;
53
- }
54
-
55
- function createUser(request: CreateUserRequest): User {
56
- const { email, name } = request; // Destructure inside
57
- // ...
58
- }
59
-
60
- // Guard clauses for early returns; happy path last
61
- function processOrder(order: Order): Result {
62
- if (!order.isValid) {
63
- return { error: "Invalid order" };
64
- }
65
- // Main logic
66
- }
67
- ```
68
-
69
44
  ## Error Handling
70
45
 
71
46
  - **Expected errors** (not found, validation failures): return `ServiceResult` (Either type) from `@clipboard-health/util-ts` instead of `try/catch`
@@ -90,30 +65,6 @@ throw new ServiceError({
90
65
  - Use `date-fns` only for timezone-agnostic timestamp math and parsing
91
66
  - Use `date-fns` comparison functions (`isBefore`, `isAfter`, `isEqual`, `isSameDay`, `compareAsc`, `compareDesc`) for all date comparisons — never use raw JS comparison operators (`>`, `<`, `===`, `>=`, `<=`) or `.getTime()` for equality/inequality checks
92
67
  - Never import `date-fns-tz`, `@date-fns/tz`, `moment`, or `moment-timezone`
93
- - In contracts, use `dateTimeSchema()` from `contract-core` for date fields — not `z.coerce.date()`, `z.string().datetime()`, or `z.date()`
94
-
95
- ```typescript
96
- import { isBefore, isAfter, isSameDay } from "date-fns";
97
-
98
- // ✅ Good — explicit, readable, handles edge cases
99
- if (isBefore(shiftStart, now)) {
100
- }
101
- if (isSameDay(createdAt, today)) {
102
- }
103
-
104
- // ❌ Bad — raw JS comparison, implicit coercion risks
105
- if (shiftStart < now) {
106
- }
107
- if (shiftStart.getTime() === today.getTime()) {
108
- }
109
- ```
110
-
111
- ## Internal Libraries
112
-
113
- - Use object arguments and object return types in library APIs; wrap exported responses in `ServiceResult`; prefer `ServiceResult` for expected errors and reserve throwing for unexpected/unrecoverable failures
114
- - Library API types must not contain `any` or bare `object`; prefer specific types over `unknown`, but allow `unknown` when type safety requires caller-side narrowing; use TypeScript generics; define the public API exclusively through `src/index.ts` exports; place non-public code in `internal/`
115
- - Strive for 100% test coverage in library code (`/* istanbul ignore next */` only for genuinely untestable lines)
116
- - When wrapping another library, design the API from first principles for our use cases; do not mirror the wrapped library's API or leak implementation details through interface names
117
68
 
118
69
  ## Rules Engine
119
70
 
@@ -1,6 +1,10 @@
1
- # About
1
+ ---
2
+ description: "Querying analytics data: dbt-mcp, Snowflake, source columns, output formatting"
3
+ ---
2
4
 
3
- General knowledge for running data analysis and querying our snowflake data warehouse.
5
+ # Analytics
6
+
7
+ General knowledge for running data analysis and querying our Snowflake data warehouse.
4
8
 
5
9
  ## Understanding dbt Model Relationships and Metadata
6
10
 
@@ -12,30 +16,30 @@ Use the dbt-mcp server to:
12
16
 
13
17
  ## Querying Snowflake Data Warehouse (using Snowflake MCP)
14
18
 
15
- - When you need to answer data-related questions or obtain analytics by querying the Snowflake data warehouse, please use the `snowflake` mcp tool.
16
- - When using the Snowflake MCP to run queries, you must set the database context properly in your queries. Use fully qualified table names or set the database context to avoid connection errors.
17
- - The `describe_object` tool in the Snowflake MCP has a bug where it misinterprets the target_object structure, treating the table name as a database name and causing 404 "database does not exist" errors. Use `run_snowflake_query` with "DESCRIBE TABLE" instead to get table schema information successfully.
19
+ - When you need to answer data-related questions or obtain analytics by querying the Snowflake data warehouse, use the `snowflake` MCP tool.
20
+ - Set the database context properly in queries: use fully qualified table names or set the database context to avoid connection errors.
21
+ - As of mid-2026, the `describe_object` tool in the Snowflake MCP has a bug where it misinterprets the target_object structure, treating the table name as a database name and causing 404 "database does not exist" errors. Use `run_snowflake_query` with "DESCRIBE TABLE" instead to get table schema information.
18
22
 
19
23
  ## Guidelines when using this knowledge
20
24
 
21
25
  - Read all of the docs.yml files to learn about the analytics schema.
22
26
  - When in doubt, read the code in the data-modeling repo to learn how each column is calculated and where the data is coming from
23
- - Strongly prefer to use mart models (defined inside the mart folder, those that don't have an int- or stg- prefix) before int- and stg- models
27
+ - Strongly prefer mart models (defined inside the mart folder, those that don't have an int- or stg- prefix) before int- and stg- models
24
28
  - Strongly prefer to query tables under the analytics schema, before querying any other schemas like airbyte_db/hevo_database
25
29
  - If unsure, confirm with the user and suggest candidate tables
26
- - If required, you might do some data analysis using python instead of pure SQL. Connect to snowflake using a python script and then use libraries like pandas, numpy, seaborn for visualization
30
+ - If required, you might do some data analysis using Python instead of pure SQL. Connect to Snowflake using a Python script and then use libraries like pandas, numpy, seaborn for visualization
27
31
 
28
32
  ## Output format
29
33
 
30
- - When running queries against snowflake and providing the user with a final answer, always show the final query that produced the result along with the result itself, so that the user is able to validate the query makes sense.
31
- - Once you've reached a final query that you need to show to the user, use get_metabase_playground_link to generate a playground link where the user can run the query themselves. Format it as a link with the 'metabase playground link' label as the link text, using slack's Markdown format. This is A MUST
34
+ - Always show the final query that produced the result along with the result itself, so the user can validate the query makes sense.
35
+ - Always use get_metabase_playground_link to generate a playground link where the user can run the query themselves; label the link "metabase playground link" (when responding in Slack, use Slack's link format).
32
36
  - Include charts or tables formatted as Markdown if needed
33
- - If the final result is a single number, make sure to show this prominently to the user so it's very easy to see
37
+ - If the final result is a single number, show it prominently so it's very easy to see
34
38
 
35
39
  ## Identifying the right columns to use and how to filter data
36
40
 
37
- For categorical columns, you might want to select distinct values for a specific column to see what the possible options are and how to filter data
38
- Read the model definitions in the dbt folders to see how that column is computed and what possible values it might have
41
+ For categorical columns, select distinct values for a specific column to see what the possible options are and how to filter data.
42
+ Read the model definitions in the dbt folders to see how that column is computed and what possible values it might have.
39
43
 
40
44
  ## Finding Source Columns for dbt Models
41
45
 
@@ -43,6 +47,9 @@ When working on dbt model modifications and you cannot find specific fields in t
43
47
 
44
48
  ## Column Discovery Best Practices
45
49
 
46
- When discovering columns in Snowflake source tables, use the full table name approach with information_schema.columns and avoid LIKE clauses for more precise results. Use queries like:
47
- `SELECT column_name, data_type FROM airbyte_database.information_schema.columns WHERE table_name = 'EVENT' AND table_schema = 'SALESFORCE' ORDER BY ordinal_position`
48
- Instead of using LIKE clauses or partial matching which can be imprecise.
50
+ When discovering columns in Snowflake source tables, query `information_schema.columns` with the full table name instead of LIKE clauses or partial matching, which can be imprecise:
51
+
52
+ ```sql
53
+ SELECT column_name, data_type FROM airbyte_database.information_schema.columns
54
+ WHERE table_name = 'EVENT' AND table_schema = 'SALESFORCE' ORDER BY ordinal_position
55
+ ```
@@ -1,3 +1,7 @@
1
+ ---
2
+ description: "Casting data types in dbt staging models"
3
+ ---
4
+
1
5
  # Casting data-types for staging models
2
6
 
3
7
  1. Use declared schemas when available