@clipboard-health/ai-rules 1.6.43 → 1.7.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/common/AGENTS.md CHANGED
@@ -1,63 +1,267 @@
1
1
  <!-- Generated by Ruler -->
2
2
 
3
- <!-- Source: .ruler/common/codeStyleAndStructure.md -->
3
+ <!-- Source: .ruler/common/commitMessages.md -->
4
4
 
5
- # Code style and structure
5
+ # Commit Messages
6
6
 
7
- - Write concise, technical TypeScript code with accurate examples.
8
- - Use functional and declarative programming patterns.
9
- - Prefer iteration and modularization over code duplication.
10
- - Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError).
11
- - Structure files: constants, types, exported functions, non-exported functions.
12
- - Avoid magic strings and numbers; define constants.
13
- - Use camelCase for files and directories (e.g., modules/shiftOffers.ts).
14
- - When declaring functions, use the `function` keyword, not `const`.
15
- - Files should read from top to bottom: `export`ed items live on top and the internal functions and methods they call go below them.
16
- - Prefer data immutability.
7
+ Follow Conventional Commits 1.0 spec for commit messages and PR titles.
17
8
 
18
- # Commit messages
9
+ <!-- Source: .ruler/common/configuration.md -->
19
10
 
20
- - Follow the Conventional Commits 1.0 spec for commit messages and in pull request titles.
11
+ # Configuration
21
12
 
22
- <!-- Source: .ruler/common/errorHandlingAndValidation.md -->
13
+ ```text
14
+ Contains secrets?
15
+ └── Yes → SSM Parameter Store
16
+ └── No → Engineers-only, tolerate 1hr propagation?
17
+ └── Yes → Hardcode with @clipboard-health/config
18
+ └── No → 1:1 with DB entity OR needs custom UI?
19
+ └── Yes → Database
20
+ └── No → LaunchDarkly feature flag
21
+ ```
23
22
 
24
- # Error handling and validation
23
+ <!-- Source: .ruler/common/featureFlags.md -->
25
24
 
26
- - Sanitize user input.
27
- - Handle errors and edge cases at the beginning of functions.
28
- - Use early returns for error conditions to avoid deeply nested if statements.
29
- - Place the happy path last in the function for improved readability.
30
- - Avoid unnecessary else statements; use the if-return pattern instead.
31
- - Use guard clauses to handle preconditions and invalid states early.
32
- - Implement proper error logging and user-friendly error messages.
33
- - Favor `@clipboard-health/util-ts`'s `Either` type for expected errors instead of `try`/`catch`.
25
+ # Feature Flags
26
+
27
+ **Naming:** `YYYY-MM-[kind]-[subject]` (e.g., `2024-03-release-new-booking-flow`)
28
+
29
+ | Kind | Purpose |
30
+ | ------------ | ------------------------ |
31
+ | `release` | Gradual rollout to 100% |
32
+ | `enable` | Kill switch |
33
+ | `experiment` | Trial for small audience |
34
+ | `configure` | Runtime config |
35
+
36
+ **Rules:**
37
+
38
+ - "Off" = default/safer value
39
+ - No permanent flags (except `configure`)
40
+ - Create archival ticket when creating flag
41
+ - Validate staging before production
42
+ - Always provide default values in code
43
+ - Clean up after full launch
44
+
45
+ <!-- Source: .ruler/common/loggingObservability.md -->
46
+
47
+ # Logging & Observability
48
+
49
+ ## Log Levels
50
+
51
+ | Level | When |
52
+ | ----- | ------------------------------------------ |
53
+ | ERROR | Required functionality broken (2am pager?) |
54
+ | WARN | Optional broken OR recovered from failure |
55
+ | INFO | Informative, ignorable during normal ops |
56
+ | DEBUG | Local only, not production |
57
+
58
+ ## Best Practices
59
+
60
+ ```typescript
61
+ // Bad
62
+ logger.error("Operation failed");
63
+ logger.error(`Operation failed for workplace ${workplaceId}`);
64
+
65
+ // Good—structured context
66
+ logger.error("Exporting urgent shifts to CSV failed", {
67
+ workplaceId,
68
+ startDate,
69
+ endDate,
70
+ });
71
+ ```
72
+
73
+ **Never log:** PII, PHI, tokens, secrets, SSN, account numbers, entire request/response/headers.
74
+
75
+ Use metrics for counting:
76
+
77
+ ```typescript
78
+ datadogMetrics.increment("negotiation.errors", { state: "New York" });
79
+ ```
80
+
81
+ <!-- Source: .ruler/common/pullRequests.md -->
82
+
83
+ # Pull Requests
84
+
85
+ **Requirements:**
86
+
87
+ 1. Clear title: change summary + ticket; Conventional Commits 1.0
88
+ 2. Thorough description: why, not just what
89
+ 3. Small & focused: single concept
90
+ 4. Tested: service tests + validation proof
91
+ 5. Passing CI
92
+
93
+ **Description:** Link ticket, context, reasoning, areas of concern.
94
+
95
+ <!-- Source: .ruler/common/security.md -->
96
+
97
+ # Security
98
+
99
+ **Secrets:**
100
+
101
+ - `.env` locally (gitignored)
102
+ - Production: AWS SSM Parameter Store
103
+ - Prefer short-lived tokens
104
+
105
+ **Naming:** `[ENV]_[VENDOR]_[TYPE]_usedBy_[CLIENT]_[SCOPE]_[CREATED_AT]_[OWNER]`
106
+
107
+ <!-- Source: .ruler/common/structuredConcurrency.md -->
108
+
109
+ # Structured Concurrency
110
+
111
+ ```typescript
112
+ // Cancellation propagation
113
+ async function fetchWithTimeout(url: string, timeoutMs: number): Promise<Response> {
114
+ const controller = new AbortController();
115
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
116
+ try {
117
+ return await fetch(url, { signal: controller.signal });
118
+ } finally {
119
+ clearTimeout(timeoutId);
120
+ }
121
+ }
122
+
123
+ // All results regardless of failures
124
+ const results = await Promise.allSettled(operations);
125
+ const succeeded = results.filter((r) => r.status === "fulfilled");
126
+ const failed = results.filter((r) => r.status === "rejected");
127
+ ```
34
128
 
35
129
  <!-- Source: .ruler/common/testing.md -->
36
130
 
37
131
  # Testing
38
132
 
39
- - Follow the Arrange-Act-Assert convention for tests with newlines between each section.
40
- - Name test variables using the `mockX`, `input`, `expected`, `actual` convention.
41
- - Aim for high test coverage, writing both positive and negative test cases.
42
- - Prefer `it.each` for multiple test cases.
43
- - Avoid conditional logic in tests.
133
+ ## Unit Tests
134
+
135
+ Use when: error handling hard to trigger black-box, concurrency scenarios, >5 variations, pure function logic.
136
+
137
+ ## Conventions
138
+
139
+ - Use `it` not `test`; `describe` for grouping
140
+ - Arrange-Act-Assert with newlines between
141
+ - Variables: `mockX`, `input`, `expected`, `actual`
142
+ - Prefer `it.each` for multiple cases
143
+ - No conditional logic in tests
44
144
 
45
145
  <!-- Source: .ruler/common/typeScript.md -->
46
146
 
47
- # TypeScript usage
48
-
49
- - Use strict-mode TypeScript for all code; prefer interfaces over types.
50
- - Avoid enums; use const maps instead.
51
- - Strive for precise types. Look for type definitions in the codebase and create your own if none exist.
52
- - Avoid using type assertions like `as` or `!` unless absolutely necessary.
53
- - Use the `unknown` type instead of `any` when the type is truly unknown.
54
- - Use an object to pass multiple function params and to return results.
55
- - Leverage union types, intersection types, and conditional types for complex type definitions.
56
- - Use mapped types and utility types (e.g., `Partial<T>`, `Pick<T>`, `Omit<T>`) to transform existing types.
57
- - Implement generic types to create reusable, flexible type definitions.
58
- - Utilize the `keyof` operator and index access types for dynamic property access.
59
- - Implement discriminated unions for type-safe handling of different object shapes where appropriate.
60
- - Use the `infer` keyword in conditional types for type inference.
61
- - Leverage `readonly` properties for function parameter immutability.
62
- - Prefer narrow types whenever possible with `as const` assertions, `typeof`, `instanceof`, `satisfies`, and custom type guards.
63
- - Implement exhaustiveness checking using `never`.
147
+ # TypeScript
148
+
149
+ ## Naming Conventions
150
+
151
+ | Element | Convention | Example |
152
+ | --------------------- | --------------------- | ---------------------------- |
153
+ | File-scope constants | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
154
+ | Acronyms in camelCase | Lowercase after first | `httpRequest`, `gpsPosition` |
155
+ | Files | Singular, dotted | `user.service.ts` |
156
+
157
+ ## Core Rules
158
+
159
+ - Strict-mode TypeScript; prefer interfaces over types
160
+ - Avoid enums—use const maps
161
+ - NEVER use `any`—use `unknown` or generics
162
+ - Avoid type assertions (`as`, `!`) unless absolutely necessary
163
+ - Use `function` keyword for declarations, not `const`
164
+ - Prefer `undefined` over `null`
165
+ - Explicit return types on functions
166
+ - Files read top-to-bottom: exports first, internal helpers below
167
+ - Boolean props: `is*`, `has*`, `should*`, `can*`
168
+ - Use const assertions for constants: `as const`
169
+
170
+ ## Types
171
+
172
+ ```typescript
173
+ // Strong typing
174
+ function process(arg: unknown) {} // Better than any
175
+ function process<T>(arg: T) {} // Best
176
+
177
+ // Nullable checks
178
+ if (foo == null) {
179
+ } // Clear intent
180
+ if (isDefined(foo)) {
181
+ } // Better with utility
182
+
183
+ // Quantity values—always unambiguous
184
+ const money = { amountInMinorUnits: 500, currencyCode: "USD" };
185
+ const durationMinutes = 30;
186
+ ```
187
+
188
+ **Type Techniques:**
189
+
190
+ - Union, intersection, conditional types for complex definitions
191
+ - Mapped types: `Partial<T>`, `Pick<T>`, `Omit<T>`
192
+ - `keyof`, index access types, discriminated unions
193
+ - `as const`, `typeof`, `instanceof`, `satisfies`, type guards
194
+ - Exhaustiveness checking with `never`
195
+ - `readonly` for parameter immutability
196
+
197
+ ## Functions
198
+
199
+ ```typescript
200
+ // Object arguments with interfaces
201
+ interface CreateUserRequest {
202
+ email: string;
203
+ name?: string;
204
+ }
205
+
206
+ function createUser(request: CreateUserRequest): User {
207
+ const { email, name } = request; // Destructure inside
208
+ // ...
209
+ }
210
+
211
+ // Guard clauses for early returns
212
+ function processOrder(order: Order): Result {
213
+ if (!order.isValid) {
214
+ return { error: "Invalid order" };
215
+ }
216
+ // Main logic
217
+ }
218
+ ```
219
+
220
+ ## Objects & Arrays
221
+
222
+ ```typescript
223
+ // Spread over Object.assign
224
+ const updated = { ...original, name: "New Name" };
225
+
226
+ // Array methods over loops (unless breaking early)
227
+ const doubled = items.map((item) => item * 2);
228
+ const sorted = items.toSorted((a, b) => a - b); // Immutable
229
+
230
+ // For early exit
231
+ for (const item of items) {
232
+ if (condition) break;
233
+ }
234
+ ```
235
+
236
+ ## Async
237
+
238
+ ```typescript
239
+ // async/await over .then()
240
+ async function fetchData(): Promise<Data> {
241
+ const response = await api.get("/data");
242
+ return response.data;
243
+ }
244
+
245
+ // Parallel
246
+ const results = await Promise.all(items.map(processItem));
247
+
248
+ // Sequential (when needed)
249
+ for (const item of items) {
250
+ // eslint-disable-next-line no-await-in-loop
251
+ await processItem(item);
252
+ }
253
+ ```
254
+
255
+ ## Classes
256
+
257
+ ```typescript
258
+ class UserService {
259
+ public async findById(request: FindByIdRequest): Promise<User> {}
260
+ private validateUser(user: User): boolean {}
261
+ }
262
+
263
+ // Extract pure functions outside classes
264
+ function formatUserName(first: string, last: string): string {
265
+ return `${first} ${last}`;
266
+ }
267
+ ```