@clipboard-health/ai-rules 2.26.0 → 2.28.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.
- package/README.md +56 -39
- package/package.json +1 -1
- package/rules/backend/architecture.md +4 -0
- package/rules/backend/asyncMessaging.md +4 -0
- package/rules/backend/infrastructure.md +4 -0
- package/rules/backend/mongodb.md +4 -33
- package/rules/backend/notifications.md +4 -0
- package/rules/backend/postgres.md +4 -0
- package/rules/backend/restApiDesign.md +10 -54
- package/rules/backend/serviceTests.md +4 -0
- package/rules/common/configuration.md +4 -0
- package/rules/common/coreLibraries.md +4 -0
- package/rules/common/featureFlags.md +4 -0
- package/rules/common/gitWorkflow.md +4 -0
- package/rules/common/libraryAuthoring.md +13 -0
- package/rules/common/loggingObservability.md +4 -0
- package/rules/common/testing.md +5 -1
- package/rules/common/typeScript.md +12 -61
- package/rules/datamodeling/analytics.md +22 -15
- package/rules/datamodeling/castingDbtStagingModels.md +4 -0
- package/rules/datamodeling/dbtModelDevelopment.md +16 -12
- package/rules/datamodeling/dbtYamlDocumentation.md +4 -0
- package/rules/frontend/{fileOrganization.md → architecture.md} +22 -1
- package/rules/frontend/customHooks.md +4 -12
- package/rules/frontend/dataFetching.md +25 -16
- package/rules/frontend/e2eTesting.md +5 -10
- package/rules/frontend/reactComponents.md +48 -42
- package/rules/frontend/styling.md +91 -6
- package/rules/frontend/testing.md +6 -35
- package/scripts/constants.js +1 -79
- package/scripts/rules.js +126 -0
- package/scripts/sync.js +20 -61
- package/rules/frontend/bottomSheets.md +0 -25
- package/rules/frontend/businessLogicPlacement.md +0 -3
- package/rules/frontend/errorHandling.md +0 -39
- package/rules/frontend/frontendTechnologyStack.md +0 -10
- package/rules/frontend/interactiveElements.md +0 -19
- package/rules/frontend/modalRoutes.md +0 -15
- 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
|
-
|
|
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
|
-
|
|
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 |
|
|
123
|
-
| ------------------------ |
|
|
124
|
-
| `backend/architecture` |
|
|
125
|
-
| `backend/asyncMessaging` |
|
|
126
|
-
| `backend/
|
|
127
|
-
| `backend/
|
|
128
|
-
| `backend/
|
|
129
|
-
| `backend/
|
|
130
|
-
| `backend/
|
|
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
|
-
###
|
|
128
|
+
### common
|
|
133
129
|
|
|
134
|
-
| Rule ID
|
|
135
|
-
|
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
141
|
-
| `
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
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 |
|
|
151
|
-
| -------------------------------------- |
|
|
152
|
-
| `datamodeling/analytics` |
|
|
153
|
-
| `datamodeling/castingDbtStagingModels` |
|
|
154
|
-
| `datamodeling/dbtModelDevelopment` | dbt
|
|
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
package/rules/backend/mongodb.md
CHANGED
|
@@ -1,26 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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,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
|
-
-
|
|
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
|
-
|
|
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 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:
|
|
@@ -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
|
package/rules/common/testing.md
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
1
|
+
---
|
|
2
|
+
description: "Querying analytics data: dbt-mcp, Snowflake, source columns, output formatting"
|
|
3
|
+
---
|
|
2
4
|
|
|
3
|
-
|
|
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,
|
|
16
|
-
-
|
|
17
|
-
-
|
|
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
|
|
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
|
|
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
|
-
-
|
|
31
|
-
-
|
|
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,
|
|
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,
|
|
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,
|
|
47
|
-
|
|
48
|
-
|
|
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,4 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
description: "Developing dbt models: naming, structure, testing"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# dbt Model Development
|
|
6
|
+
|
|
7
|
+
Read the following docs in the data-modeling repo; they define our modeling rules, patterns, and safety constraints:
|
|
2
8
|
|
|
3
9
|
- CONTRIBUTING.md
|
|
4
10
|
- dbt_style_guide.md
|
|
@@ -7,21 +13,19 @@
|
|
|
7
13
|
- models/DEVIN_ANALYST_FOLDER_STRUCTURE.md
|
|
8
14
|
- .github/pull_request_template.md
|
|
9
15
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
# Key best practices to follow
|
|
16
|
+
## Key best practices to follow
|
|
13
17
|
|
|
14
|
-
-
|
|
18
|
+
- When running dbt commands, reuse the existing `SNOWFLAKE_SCHEMA` environment variable value if it is already set — it is a unique per-session schema; never overwrite it with a hardcoded value.
|
|
15
19
|
- Use `dbt build` to verify your changes.
|
|
16
|
-
- ALL dbt staging models must have strictly defined datatypes
|
|
17
|
-
- When adding new fields to tables keep the original source field name format, but remove any custom field prefix (
|
|
18
|
-
- If a source table doesn't exist
|
|
19
|
-
- A model must always have a primary/unique key. If there's no obvious one,
|
|
20
|
+
- ALL dbt staging models must have strictly defined datatypes (see the `datamodeling/castingDbtStagingModels` rule). These datatypes need to be defined in the YAML documentation too.
|
|
21
|
+
- When adding new fields to tables keep the original source field name format, but remove any custom field prefix (`__c`). For example `assignment_type__c` should be renamed to `assignment_type`. Verify column names against the source table before referencing them — do not guess.
|
|
22
|
+
- If a source table doesn't exist, tell the user to ask the data-team to ingest it via the relevant ETL tool.
|
|
23
|
+
- A model must always have a primary/unique key. If there's no obvious one, create a surrogate key using a combination of fields and by looking at the data. Use `dbt_utils.generate_surrogate_key` to do so.
|
|
20
24
|
- Snapshots must be configured in YAML files (dbt 1.9+ style), not in SQL files. Define the `config` block and `relation` property in the snapshot's `.yml` file instead of using `{% snapshot %}` blocks in `.sql` files.
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
## When creating PRs for the data-modeling repo
|
|
23
27
|
|
|
24
|
-
- Read .github/pull_request_template.md. It contains the structure of how
|
|
25
|
-
- Under the **Validation of models** section in the PR
|
|
28
|
+
- Read .github/pull_request_template.md. It contains the structure of how to format the PR description.
|
|
29
|
+
- Under the **Validation of models** section in the PR description, print the full table names of the dev models you've built, so it's easy to review the data.
|
|
26
30
|
- Keep PR descriptions concise and focused. A reader should be able to quickly understand the intent and scope of the change.
|
|
27
31
|
- Include the **dbt commands run** to validate the models.
|