You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(claude): trim rules the model already follows
Prompted by Anthropic cutting 80% of Claude Code's own system prompt: as
models improve, generic instructions dilute the project-specific ones
rather than adding to them.
- Drop the LINQ and comment-style rules; both are default behavior and
only dilute the project-specific rules around them
- Reduce the O(n^2) and bug-fix rules to their non-obvious half
- Drop "never stage until after code review". It blocked unattended
agentic sessions, and the failure it guarded against costs an amend on
an unmerged branch
- Reduce the line-ending and working-note rules to statements of fact,
now that .gitattributes and .gitignore enforce them deterministically
- Replace all em dashes with sentence-appropriate punctuation
Copy file name to clipboardExpand all lines: CLAUDE.md
+16-19Lines changed: 16 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ When I ask a question or make an observation, respond with an answer - do NOT ju
4
4
5
5
## Environment & Commands
6
6
7
-
**Line endings must be CRLF (`\r\n`)** - never use bare LF.
7
+
**Line endings need no attention.**`.gitattributes` normalizes every text file to LF in the repository on commit, whatever the working copy has. Do not "fix" a file's endings; see [HOOKS.md](HOOKS.md).
8
8
9
9
- Run via npm scripts, never direct .NET/dotnet commands (avoids lock-file conflicts): Dev `npm run dev` | Test `npm run test` (`test:backend`, `test:frontend`; single test: `npm run test:backend -- <TestClassName>` / `npm run test:frontend -- <file-pattern>`) | Lint `npm run lint <path>` | Build `npm run verify:build`
10
10
-**Stale cache**: Add `-- --clear-cache` to `verify:build` or `lint` if builds fail with cached errors
@@ -14,46 +14,43 @@ When I ask a question or make an observation, respond with an answer - do NOT ju
14
14
-**Backend**: ASP.NET 10, `web/Areas/{AreaName}/` | **Frontend**: Vue 3 multi-SPA, Vite → `wwwroot/vue/`
15
15
-**DB**: SQL Server 2016 + EF Core (CTS, RAPS, AAUD schemas) | **Auth**: CAS + `[Permission]` (see API & Cross-Environment)
16
16
-**Identity:**`AaudUser.AaudUserId` = `Person.PersonId`. If mismatched, TEST DB needs refresh.
17
-
-**Design system (UI)**: All UI rules (colors, typography, components, `<main>` landmark, WCAG-AA contrast) live in [DESIGN.md](DESIGN.md) — read it before building or changing UI. Always use Quasar components.
17
+
-**Design system (UI)**: All UI rules (colors, typography, components, `<main>` landmark, WCAG-AA contrast) live in [DESIGN.md](DESIGN.md). Read it before building or changing UI. Always use Quasar components.
18
18
-**VueUse**: Prefer VueUse composables over hand-rolled reactive logic.
19
-
-**O(n²) lookups (JS & C#)**: Never nest per-item searches (`.find()`/`.some()`/`.FirstOrDefault()`/`.Any()`) inside a loop over another growable list. Pre-build a `Map`/`Set`/`Dictionary` once, then look up in the loop. Small fixed reference lists (~10 items) are fine. In EF this is N+1 — see Correlated subqueries.
20
-
-**Plurals**: Use `inflect("word", count)` from the `inflection` package — never hand-roll ternaries for noun pluralization
19
+
-**O(n²) lookups**: Pre-build a `Map`/`Set`/`Dictionary` rather than nesting `.find()`/`.FirstOrDefault()` in a loop over a growable list. In EF this is N+1, see Correlated subqueries.
20
+
-**Plurals**: Use `inflect("word", count)` from the `inflection` package, never hand-roll ternaries for noun pluralization
21
21
22
22
## Database & EF Core
23
23
24
-
-**SQL Server 2016** — no `STRING_AGG`, `TRIM`, `CONCAT_WS`, `GREATEST/LEAST`
24
+
-**SQL Server 2016**: no `STRING_AGG`, `TRIM`, `CONCAT_WS`, `GREATEST/LEAST`
25
25
- Prefer EF entities over raw SQL. Raw SQL only for non-EF tables via `GetConnectionString()`. Never mix raw SQL + EF entities (causes auth failures).
26
-
-**Read-only queries**: Always `.AsNoTracking()` | `.Include()` before `.Select()` is unnecessary — EF resolves navigations in projections
27
-
-**Correlated subqueries**: Avoid `.Any()` on large tables inside `.Where()`/`.CountAsync()` — pre-load ID sets then use `.Contains()`, or replace with `.Join()`
26
+
-**Read-only queries**: Always `.AsNoTracking()` | `.Include()` before `.Select()` is unnecessary, EF resolves navigations in projections
27
+
-**Correlated subqueries**: Avoid `.Any()` on large tables inside `.Where()`/`.CountAsync()`: pre-load ID sets then use `.Contains()`, or replace with `.Join()`
28
28
-**`.Contains()` with large collections (10+)**: Wrap with `EF.Parameter()` for `OPENJSON` translation: `.Where(x => EF.Parameter(largeList).Contains(x.Id))`. Small collections (<10) are fine without it.
29
-
-**Thread safety**: DbContext not thread-safe — no parallel EF queries
29
+
-**Thread safety**: DbContext not thread-safe, no parallel EF queries
30
30
31
31
## API & Cross-Environment
32
32
33
33
-**Routes**: Absolute `/api/{area}/{controller}` + `ApiController` base. Never `[Area]` on APIs (causes 403)
34
34
-**Frontend API calls**: Service layer + `useFetch()`, never raw `fetch()` (must unwrap `{ result, success }`)
-**API URL**: `${import.meta.env.VITE_API_URL}`, never hardcode `/api/` (TEST uses `/2/` prefix)
36
36
-**Subpath PathBase (`/2`)**: TEST/PROD run VIPER 2 under a `/2` PathBase (IIS sub-app), legacy VIPER 1 at `/`; with no base locally, these bugs surface only on TEST/PROD (not in unit tests). Use `~/` for app-root redirects, never bare `/` (escapes to the legacy site). Guards matching root-relative paths (`/api`, `/welcome`) must strip the base off the base-prefixed `ReturnUrl` (`/2/...`) or use `Request.PathBase`. `RedirectToAction`, `@Url.Content("~/")`, and tag-helpers include the base; raw string paths (`Redirect("/x")`, `returnUrl.StartsWith("/api")`) don't.
37
-
-**Auth**: `[Permission(Allow = "SVMSecure.{Area}")]`, or finer `"SVMSecure.{Area}.{Permission}"` — authenticate before validating params
37
+
-**Auth**: `[Permission(Allow = "SVMSecure.{Area}")]`, or finer `"SVMSecure.{Area}.{Permission}"`. Authenticate before validating params
38
38
39
39
## C# Standards
40
40
41
41
-**Exceptions**: Catch specific types (`DbUpdateException`, `SqlException`, `InvalidOperationException`). Never generic `catch (Exception ex)`.
42
-
-**Paths**: `Path.Join()` not `Path.Combine()` | **DateTime**: prefer `DateTimeKind.Local`
43
-
-**LINQ**: `.Where()` for filtering (not `if` inside foreach), `.Select()` for mapping (not foreach + Add)
44
-
-**Mapperly**: Prefer over manual property mapping. Static partial mapper class per area with `[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.None)]`. Use `[MapperIgnoreTarget]` for computed properties, manual wrappers for transforms. Align entity/DTO names — use EF `HasColumnName()` to decouple from DB columns.
45
-
-**Scrutor**: Convention-based DI auto-registers `*Service`/`*Validator` from configured namespaces — prefer over manual `AddScoped`. Follow `IFooService`/`FooService` naming. Explicit `AddScoped` before Scrutor takes precedence (`RegistrationStrategy.Skip`).
-**Bug fixes**: Verify ALL code paths using affected logic. Check for duplicate/parallel implementations and fix consistently or DRY into shared method.
42
+
-**Paths**: `Path.Join()` not `Path.Combine()` (`Combine` silently discards everything before a rooted segment) | **DateTime**: prefer `DateTimeKind.Local`
43
+
-**Mapperly**: Prefer over manual property mapping. Static partial mapper class per area with `[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.None)]`. Use `[MapperIgnoreTarget]` for computed properties, manual wrappers for transforms. Align entity/DTO names: use EF `HasColumnName()` to decouple from DB columns.
44
+
-**Scrutor**: Convention-based DI auto-registers `*Service`/`*Validator` from configured namespaces, prefer over manual `AddScoped`. Follow `IFooService`/`FooService` naming. Explicit `AddScoped` before Scrutor takes precedence (`RegistrationStrategy.Skip`).
45
+
-**Bug fixes**: Check for duplicate/parallel implementations of the affected logic and fix consistently, or DRY into a shared method.
48
46
-**Log injection**: Sanitize user input before logging via `LogSanitizer` (`SanitizeId()`, `SanitizeString()`, `SanitizeYear()`). Skip hard-coded strings, enums, DB values.
49
47
50
48
## Testing & Git
51
49
52
50
-**UI**: Test UI changes with Playwright MCP (modals, forms, keyboard nav)
53
-
-**API**: Use Playwright MCP to visit endpoints — APIs require browser auth, `curl` fails
54
-
-**Git**: NEVER stage files until after code review. Workflow: changes → test → lint → summary → approval → stage
51
+
-**API**: Use Playwright MCP to visit endpoints, APIs require browser auth, `curl` fails
55
52
-**Branch & merge flow**: Branch off `main`, named `feature/`|`fix/`|etc. plus the JIRA ticket if applicable (e.g. `feature/VPR-104-clinical-scheduler`). After code review, merge into `Development` and push, which deploys to TEST. After the PR is approved on TEST, merge into `main`. Every change goes through `Development` first.
56
53
-**Never branch off `Development`**: it is a merge/deploy target, never a base. A branch being "behind `Development`" is expected and not a concern (you never sync or rebase from it). Its history is messy by design and never rewritten.
57
54
-**Squash during review**: If a branch is still unmerged and worked by a single developer, squash code-review fixes into the relevant existing commit for cleaner history rather than stacking "address review" commits.
58
-
-**Plan/smoketest notes**: `PLAN-*.md` and `SMOKETEST-*.md`files at the repo root are local working notes — never stage or commit them. They are intentionally left untracked.
55
+
-**Plan/smoketest notes**: `PLAN-*.md` and `SMOKETEST-*.md` at the repo root are local working notes, gitignored by design.
59
56
-**Commit messages**: Conventional Commits `type(scope): subject` (`feat`|`fix`|`refactor`|`docs`|`test`|`chore`; prefer `feat` for new behavior), ticket ID from branch as prefix (e.g. `VPR-104 fix(a11y): ...`). Subject: imperative, max 72 chars, no trailing period, intent not implementation. Body only when the subject is insufficient: `-` bullets that each earn their place (skip plumbing/helpers/test scaffolding), wrapped at 72.
0 commit comments