Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
08c2adf
docs: Add plan and review for PR #3316 post-merge fixes
KATO-Hiro Apr 6, 2026
2687ff0
test: migrate process.env manual stubs to vi.stubEnv in atcoder_verif…
KATO-Hiro Apr 6, 2026
e50bb41
test: add unit tests for getUser, getUserById, and deleteUser
KATO-Hiro Apr 6, 2026
00cbd6c
refactor: extract MIN_VOTES_FOR_STATISTICS to client-safe constant an…
KATO-Hiro Apr 6, 2026
c5b497a
docs: update plan - use internal_clients/vote_grade.ts instead of ser…
KATO-Hiro Apr 6, 2026
e117eb5
refactor: add return value to upsertVoteGradeTables and use it in vot…
KATO-Hiro Apr 6, 2026
9d4ec03
fix: handle RecordNotFound error in setTaskGrade action for vote_mana…
KATO-Hiro Apr 6, 2026
b12edec
feat: add loading state to SubmissionButton via FormWrapper context (…
KATO-Hiro Apr 6, 2026
a24be95
feat: add button gap and replace custom clipboard with Flowbite Clipb…
KATO-Hiro Apr 6, 2026
256bf34
fix: ensure isSubmitting is reset even when form update throws exception
KATO-Hiro Apr 6, 2026
c107ebf
docs: fix comment wording and remove redundant type assertion
KATO-Hiro Apr 6, 2026
5b23fe1
feat: create vote_grade internal client with fetchMyVote, submitVote,…
KATO-Hiro Apr 6, 2026
65a6bef
refactor: use vote_grade.ts in VotableGrade and add AbortController f…
KATO-Hiro Apr 6, 2026
696e6c0
fix: update E2E test selectors and add initial state check for votes …
KATO-Hiro Apr 6, 2026
e037ab5
feat: add CHECK constraints for vote grade models (count >= 0, grade …
KATO-Hiro Apr 6, 2026
2a4aecf
docs: add new coding rules derived from PR #3316 review (SSR, FK, aut…
KATO-Hiro Apr 6, 2026
70a0767
fix: address CodeRabbit findings - remove misleading test and fix For…
KATO-Hiro Apr 6, 2026
6a541e4
Merge branch 'staging' of github.com:AtCoder-NoviSteps/AtCoderNoviSte…
KATO-Hiro Apr 6, 2026
0fc88a1
refactor: rename PrismaUserWithAccount to UserWithAtCoderAccount to r…
KATO-Hiro Apr 8, 2026
0e124b8
refactor: use UUID format for SAMPLE_USER_ID and document Lucia depen…
KATO-Hiro Apr 8, 2026
bc139d8
fix: use Roles.USER enum instead of string literal in test fixtures
KATO-Hiro Apr 8, 2026
89b22c4
refactor: extract mock helpers and use Partial types to avoid any/nev…
KATO-Hiro Apr 8, 2026
5b2023f
test: add error case for generate function and organize describe bloc…
KATO-Hiro Apr 8, 2026
d64ef41
refactor: extract mockFindUniqueOrThrowError helper to avoid mock set…
KATO-Hiro Apr 8, 2026
5682ed2
test: organize validate describe block by scenario
KATO-Hiro Apr 8, 2026
f879ce4
test: reorganize validate describe blocks by 正常系/異常系
KATO-Hiro Apr 8, 2026
1ff1e19
test: split abnormal cases by return value (false vs throws)
KATO-Hiro Apr 8, 2026
2d8a702
test: organize reset describe block and add error case
KATO-Hiro Apr 8, 2026
da0eddf
docs: formalize internal_clients and test organization patterns
KATO-Hiro Apr 8, 2026
c205d2f
test: reorganize users.test.ts with fixtures and organize describe bl…
KATO-Hiro Apr 8, 2026
3355d24
refactor: translate SubmissionButton comment to English
KATO-Hiro Apr 8, 2026
ef1977c
feat: add color prop to SubmissionButton and improve ButtonColor type…
KATO-Hiro Apr 8, 2026
68ca9c0
test: refactor vote_grade.test.ts with HTTP mock helpers and parametr…
KATO-Hiro Apr 8, 2026
609addf
refactor: move getBaseUrl helper after exported functions in vote_gra…
KATO-Hiro Apr 8, 2026
556822e
chore: fix order
KATO-Hiro Apr 8, 2026
2a5cd61
docs: clean up completed plan.md for PR #3316 review
KATO-Hiro Apr 8, 2026
e9dfab8
docs: update Task 10 plan for votes E2E test fix
KATO-Hiro Apr 8, 2026
40a4818
fix: repair vote detail E2E tests broken by search-first UI and stale…
KATO-Hiro Apr 8, 2026
ab4d948
docs(rules): add E2E lessons from votes test fix
KATO-Hiro Apr 8, 2026
e135750
style: fix formatting in testing-e2e.md
KATO-Hiro Apr 8, 2026
222d409
docs: add CodeRabbit findings to plan.md
KATO-Hiro Apr 8, 2026
62e9fa1
docs: add implementation plan for CodeRabbit findings
KATO-Hiro Apr 8, 2026
2d3494f
docs(rules): consolidate and streamline coding standards
KATO-Hiro Apr 8, 2026
fb4d1a6
fix: implement CodeRabbit findings #1-10 (Phase 1-4)
KATO-Hiro Apr 9, 2026
4f16134
docs: remove completed PR #3316 review notes
KATO-Hiro Apr 9, 2026
1f1ab4b
fix: implement CodeRabbit findings (Phase 2)
KATO-Hiro Apr 9, 2026
bae4e80
fix(migration): fix failed vote grade CHECK constraint migration in s…
KATO-Hiro Apr 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 44 additions & 123 deletions .claude/rules/coding-style.md
Original file line number Diff line number Diff line change
@@ -1,143 +1,64 @@
# Coding Style

## Plan Time

### Pre-Implementation Layer Check

Before writing new logic, decide which layer it belongs to. Run this check at plan time (design/architecture phase, before writing any code):

| Layer | Directory | Key constraints |
| -------------- | ------------------------------------------------------ | -------------------------------------------------------------------------- |
| DB schema | `prisma/` | Migrations are immutable after apply |
| DB access | `src/lib/server/` | Server-only; never import in client code |
| Validation | `src/**/zod/` | `z.number().int()` for Int fields; comment dual-enforcement with SQL CHECK |
| Domain types | `src/**/types/` (`_types/` inside `src/routes/`) | Plural aliases; TSDoc on every export; avoid `any`; see alternatives |
| Test data | `src/**/fixtures/` (`_fixtures/` inside `src/routes/`) | Write before implementation (TDD); use realistic values |
| Business logic | `src/**/services/` | Return pure values or `null`; no `Response`/`json()` |
| Pure utilities | `src/**/utils/` (`_utils/` inside `src/routes/`) | No side effects; adjacent unit test required |
| State | `src/**/stores/` | `.svelte.ts`; class + `$state()`; singleton export |
| Route handlers | `src/routes/` | Page: `redirect()`; API: `error()` |
| UI components | `src/**/*.svelte` | Svelte 5 Runes; business logic → `utils/` |

## Code Structure

### Naming

- **Abbreviations**: avoid non-standard abbreviations (`res` → `response`, `btn` → `button`). When in doubt, spell it out.
- **Lambda parameters**: no single-character names (e.g., use `placement`, `workbook`). Iterator index `i` is the only exception.
- **`upsert`**: only use when the implementation performs both insert and update. For insert-only, use `initialize`, `seed`, or another accurate verb.
- **Function verbs**: every function name must start with a verb. Noun-only names (`pointOnCircle`, `arcPath`) are ambiguous — use `calcPointOnCircle`, `buildArcPath`, etc. Common prefixes: `get` (read existing), `build`/`create` (construct new), `calc`/`compute` (derive by formula), `update`, `fetch`, `resolve`.
- **`any`**: Before using `any`, check the value's origin. If unavoidable: use `ReturnType<typeof fn>` for complex returns, `Partial<T>` for partial objects, `obj as T & { prop: U }` for property extension. Last resort: `as unknown as T`.

- **UI labels**: if a label does not match actual behavior, update it or add an inline comment explaining the intentional mismatch.
- **Constant names**: reflect what the value IS (content), not what it is used for (purpose). e.g., a set holding all enum tab values is `EXISTING_TABS`, not `VALID_TABS`.
- **New files**: before naming a new file or directory, grep the relevant `src/` directory to confirm existing conventions. Confirm at plan time, not during implementation:
- Custom files in routes (utilities, helpers, etc.): `snake_case` (e.g., `user_profile.ts`)
- SvelteKit special files: follow framework conventions (`+page.svelte`, `+page.server.ts`, `+server.ts`)
- Helper directories inside `src/routes/`: underscore-prefixed (`_utils/`, `_types/`, `_fixtures/`, `_components/`)
- Other directories: `kebab-case` (e.g., `contest-table/`)

### Syntax

- **Braces**: always use braces for single-statement `if` blocks. Never `if () return;` — write `if () { return; }`.
- **Domain types over `string`**: when the Prisma schema uses an enum (e.g. `grade: TaskGrade`), the corresponding app-layer type must use the same enum — not `string`. A loose `string` type hides misspellings in fixtures and forces `as TaskGrade` casts throughout the codebase. When a field comes from an external source (form data, query params), validate and narrow it at the boundary; inside the app it must always be the domain type.
- **Plural type aliases**: define `type Placements = Placement[]` instead of using `Placement[]` directly in signatures and variables.
- **Empty `catch` blocks**: never use `catch { }` to silence errors. Either re-throw, log, or add a comment justifying suppression. Silent swallowing hides bugs.
## Pre-Implementation Layer Check

```typescript
try { ... } catch (error) { console.error('...'); throw error; } // good
try { localStorage.setItem(key, value); } catch { /* unavailable */ } // good + comment
```

### No Hard-Coded Values

Extract magic numbers and strings to named constants. Never embed literal values whose meaning is not self-evident from the type or immediate context.

```typescript
// Bad: magic literals embedded inline
if (grade >= 11) { ... }

const response = await fetch('/api/workbooks/submit', options);

// Good
const MIN_GRADE = 11;
const SUBMIT_URL = '/api/workbooks/submit';

if (grade >= MIN_GRADE) { ... }

const response = await fetch(SUBMIT_URL, options);
```

Place constants at the top of the file, or in a dedicated `constants/` module when shared across files.

### Function Ordering

Within a file, order declarations as follows:

1. Exported functions and classes (public API first)
2. Internal helper functions (supporting the exports above)
Before writing logic, decide which layer it belongs to:

Place a private helper immediately after the single export that uses it. Place helpers shared by two or more exports at the end of the file.
| Layer | Directory | Constraints |
| -------------- | ------------------------------------------- | ------------------------------------------------------- |
| DB schema | `prisma/` | Migrations immutable after apply |
| DB access | `src/lib/server/` | Server-only; never in client |
| Validation | `src/**/zod/` | `z.number().int()` for Int; dual-enforce with SQL CHECK |
| Domain types | `src/**/types/` | Plural aliases; TSDoc; avoid `any` |
| Test data | `src/**/fixtures/` | Write before impl (TDD); realistic values |
| Business logic | `src/**/services/` | Pure values/`null`; no `Response`/`json()` |
| External APIs | `src/lib/clients/` or `*/internal_clients/` | Shared → `lib`; feature-scoped → `internal_clients` |
| Utilities | `src/**/utils/` | No side effects; adjacent unit test required |
| State | `src/**/stores/` | `.svelte.ts`; class + `$state()`; singleton export |
| Route handlers | `src/routes/` | Page: `redirect()`; API: `error()` |
| Components | `src/**/*.svelte` | Svelte 5 Runes; logic → `utils/` |

### URL Parameter Patterns
## Naming

#### null-as-ALL: Omitting Params for "All" State
- **No abbreviations** unless standard (e.g., `id`, `url`)
- **Lambda params**: no single-char (except `i` for loops)
- **Function verbs**: `get` (read), `build`/`create` (construct), `calc`/`compute` (derive), `update`, `fetch`, `resolve`
- **Domain enums**: use domain type, not `string` (validates at boundary, stays typed inside)
- **Constants**: reflect content not purpose; extract magic numbers/strings
- **Files**: `snake_case` in routes, `kebab-case` dirs, underscore-prefix helpers (`_utils/`, `_types/`)

When a filter has an "all" or "unfiltered" state, omit the parameter entirely rather than using a magic value (e.g., "ALL", "\*").
## Syntax

**Pattern:**
- **Braces**: always for single-statement `if` blocks
- **Catch blocks**: never silent; re-throw, log, or comment
- **Plural aliases**: `type Items = Item[]` in signatures
- **TSDoc**: every export; `@param`/`@returns` when non-obvious

- Parse function defaults to `null` when param is absent
- `null` → "show all" (no filter applied)
- URL: `/workbooks?tab=solution` (no `categories=`)
- Browser back button naturally restores default "all" view
## Type Guards at API Boundaries

**Benefit:** Cleaner URLs, intuitive history behavior, smaller sessionStorage footprint.

**Example:** `parseWorkBookCategory()` defaults to `null` = all categories

### Type Guards: Precise Narrowing for Excluded Values

When a type guard intentionally excludes enum members, use `Exclude<T, 'VALUE'>` in the return type to match runtime behavior. Caller code then trusts the type system; no `as` casts needed.

### Layer-Specific Responsibility: Normalization vs Filtering

When filtering data by multiple criteria (format + role):

- **Service layer**: Normalize raw data format (e.g., `null → PENDING`). Return all data; stay framework-agnostic and testable.
- **UI layer**: Filter by role/context (e.g., "admin sees PENDING, user doesn't"). Apply display rules in component.

**Benefit**: Services remain pure and unit-testable without mocking roles; UI logic explicit in one place.

### Function Composition: Single Responsibility

Separate independent transformations into distinct functions. Compose at call site.
When receiving enum values from external APIs, validate with a type guard:

```typescript
// groupBySolutionCategory(): pure grouping (testable)
// filterGroupsByRole(): pure filtering by role (testable)
let filtered = $derived(filterGroupsByRole(groupBySolutionCategory(workbooks, map), role));
// Good: `isValidTaskGrade(value): value is TaskGrade`
const grade = isValidTaskGrade(data.grade) ? data.grade : null;
```

**Benefit**: Each function testable in isolation; reusable in different contexts.

## Documentation

### Language Policy
Extract to `src/lib/utils/` with adjacent tests.

Write all project documentation (plans, dev-notes, guides, refactor notes) in Japanese. Write all source code comments, TSDoc, commit messages, and test titles in English. This keeps documentation readable for the team while keeping code comments universally accessible and searchable.
## Dead Code: Three-Condition Rule

**Exception**: The `## CodeRabbit Findings` section in `plan.md` must quote findings verbatim in their original language (English). Do not translate CodeRabbit output.
Delete function only if: (1) zero callers, (2) replacement exists, (3) dependent fields also deleted.

### TSDoc

Add TSDoc to every exported function, type, class. Minimum: `@param` (non-obvious), `@returns` (not evident from type). One-liner OK; multi-line for complex behavior only.

### Documentation
## Documentation

Write plans/dev-notes/guides in Japanese. Source code comments in English. Always specify language on code blocks (e.g., `typescript`, `sql`, `bash`). For Svelte 5 unclear behavior: fetch official docs via WebFetch, not training knowledge.
- **Plans/dev-notes**: Japanese
- **Code/commits/tests**: English
- **TSDoc**: required on all exports
- **Code blocks**: specify language (`typescript`, `sql`, `bash`)

## Security & Code Review
## Security

- **Logging**: No user-identifiable data in `console.log`. Single authoritative log in service layer.
- **CodeRabbit**: Run after all phases complete. Write `critical`/`high`/`medium` findings to `plan.md` verbatim; defer `nitpick` to PR CI.
- No user-identifiable data in logs
- No Prisma imports in route handlers
- Validate input at system boundaries
- Return safe defaults on service errors
31 changes: 29 additions & 2 deletions .claude/rules/prisma-db.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,32 @@ automatically excluded. This is not an IS NOT NULL check; the mechanism is the J
When documenting this behavior, write "excluded by INNER JOIN" rather than
"implicitly includes IS NOT NULL".

## FK Relations: Always Define @relation

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.

```prisma
// Bad: FK without @relation
userId String

// Good: explicit @relation generates FK constraint
userId String
user User @relation(fields: [userId], references: [id])
```

## DB-Level Value Constraints

Add `CHECK` constraints via manual migration SQL. Document with inline `schema.prisma` comments (e.g., `/// CHECK: count >= 0`). Note: `prisma-erd-generator` may overwrite `ERD.md` on each migration—always keep the constraint definition in `schema.prisma` as the source of truth.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
## Service Layer Error Handling

Catch Prisma errors in service functions, return domain values:

- `P2025` (record not found) → `null` (no exception)
- Other errors → re-throw (caller handles as 500)

This removes Prisma imports from route handlers and enables easy testing with mocked returns.

## Dual-Enforcement Constraints

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:
Expand All @@ -99,9 +125,10 @@ Prisma does not support `@@check`. To add one:

1. `pnpm exec prisma migrate dev --create-only --name <description>` — generate migration without applying
2. Edit the generated `migration.sql` to add the CHECK constraint manually
3. `pnpm exec prisma migrate dev` — apply
3. Add an inline comment in `schema.prisma` (e.g., `/// CHECK: constraint description`)
4. `pnpm exec prisma migrate dev` — apply

Document the constraint in `prisma/ERD.md` (the only place it's visible):
For visibility, also document complex constraints in `prisma/ERD.md`:

```mermaid
%% XOR constraint: workbookplacement_xor_grade_category — exactly one of taskGrade or solutionCategory must be non-null
Expand Down
Loading
Loading