Skip to content

Commit 2a4aecf

Browse files
committed
docs: add new coding rules derived from PR #3316 review (SSR, FK, auth, env stubs)
1 parent e037ab5 commit 2a4aecf

4 files changed

Lines changed: 68 additions & 0 deletions

File tree

.claude/rules/coding-style.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,19 @@ Write plans/dev-notes/guides in Japanese. Source code comments in English. Alway
141141

142142
- **Logging**: No user-identifiable data in `console.log`. Single authoritative log in service layer.
143143
- **CodeRabbit**: Run after all phases complete. Write `critical`/`high`/`medium` findings to `plan.md` verbatim; defer `nitpick` to PR CI.
144+
145+
## `+page.server.ts` load() Error Handling
146+
147+
Wrap calls to service functions in try-catch and return safe default values on failure, preventing a single service error from crashing the entire page.
148+
149+
## Auth: Action Audit
150+
151+
When adding an auth guard to one action in `+page.server.ts`, audit all other actions in the same file. Asymmetric guards (some actions protected, others not) are a recurring pattern of vulnerability.
152+
153+
## Auth: success Flag and message Consistency
154+
155+
When an action returns `success: false`, the `message` and `message_type` must also reflect failure. A success flag contradicting the message is a silent bug.
156+
157+
## Dead Code Deletion: Three-Condition Rule
158+
159+
Before deleting a function, grep the full project for callers. Deletion is safe only when all three conditions hold: (1) zero callers, (2) a replacement implementation exists, (3) any fields this function wrote to are also being deleted.

.claude/rules/prisma-db.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,28 @@ automatically excluded. This is not an IS NOT NULL check; the mechanism is the J
8383
When documenting this behavior, write "excluded by INNER JOIN" rather than
8484
"implicitly includes IS NOT NULL".
8585

86+
## FK Relations: Always Define @relation
87+
88+
Any field that references another model's ID must have an explicit `@relation` defined. Without `@relation`, Prisma does not generate FK constraints automatically, leading to referential integrity gaps.
89+
90+
```prisma
91+
// Bad: FK without @relation
92+
userId String
93+
94+
// Good: explicit @relation generates FK constraint
95+
userId String
96+
user User @relation(fields: [userId], references: [id])
97+
```
98+
99+
## DB-Level Value Constraints
100+
101+
Add `CHECK` constraints (via manual migration SQL) for:
102+
103+
- `count` fields that must be non-negative (`count >= 0`)
104+
- Enum fields where specific values are invalid at the DB level (e.g. `grade != 'PENDING'`)
105+
106+
Document every `CHECK` constraint in `prisma/ERD.md` — it is the only place they are visible outside migration SQL.
107+
86108
## Dual-Enforcement Constraints
87109

88110
When the same constraint is enforced in both Zod (early validation) and SQL `CHECK` (last line of defense), add an inline comment stating each layer's role and the obligation to keep them in sync:

.claude/rules/svelte-components.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,11 @@ const atCoderAccount = $derived(data.atCoderAccount);
225225
```
226226

227227
`data` is a reactive prop that SvelteKit updates after each form action. A plain assignment captures the initial value only.
228+
229+
## SSR Safety: Non-Deterministic IDs
230+
231+
Do not use `crypto.randomUUID()` or `Math.random()` in component initialization code that runs during SSR. These produce different values on server and client, causing hydration mismatches. Derive component IDs deterministically from prop values (e.g. `const componentId = item.task_id`).
232+
233+
## `{#each}` — Key Expression Required
234+
235+
Every `{#each}` block must include a key expression `(item.id)`. Keyless `{#each}` causes incorrect DOM reuse and hard-to-debug update bugs. Prefer a unique domain field; fall back to the index `(i)` only when no unique field exists.

.claude/rules/testing.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,25 @@ When a service mixes DB operations and pure functions, split into `crud.ts` (DB;
109109
## HTTP Mocking
110110

111111
Use Nock for external HTTP calls. See `src/test/lib/clients/` for examples.
112+
113+
## Environment Variable Stubs
114+
115+
Use `vi.stubEnv(key, value)` + `vi.unstubAllEnvs()` instead of manually assigning `process.env[key]` and deleting it in cleanup. `vi.stubEnv` syncs `import.meta.env` as well and accurately restores the original value:
116+
117+
```typescript
118+
// Bad
119+
beforeEach(() => {
120+
process.env.MY_URL = 'https://example.com';
121+
});
122+
afterEach(() => {
123+
delete process.env.MY_URL;
124+
});
125+
126+
// Good
127+
beforeEach(() => {
128+
vi.stubEnv('MY_URL', 'https://example.com');
129+
});
130+
afterEach(() => {
131+
vi.unstubAllEnvs();
132+
});
133+
```

0 commit comments

Comments
 (0)