Skip to content

Commit 574e154

Browse files
committed
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
1 parent 8c5d317 commit 574e154

1 file changed

Lines changed: 16 additions & 19 deletions

File tree

CLAUDE.md

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ When I ask a question or make an observation, respond with an answer - do NOT ju
44

55
## Environment & Commands
66

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).
88

99
- 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`
1010
- **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
1414
- **Backend**: ASP.NET 10, `web/Areas/{AreaName}/` | **Frontend**: Vue 3 multi-SPA, Vite → `wwwroot/vue/`
1515
- **DB**: SQL Server 2016 + EF Core (CTS, RAPS, AAUD schemas) | **Auth**: CAS + `[Permission]` (see API & Cross-Environment)
1616
- **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.
1818
- **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
2121

2222
## Database & EF Core
2323

24-
- **SQL Server 2016** no `STRING_AGG`, `TRIM`, `CONCAT_WS`, `GREATEST/LEAST`
24+
- **SQL Server 2016**: no `STRING_AGG`, `TRIM`, `CONCAT_WS`, `GREATEST/LEAST`
2525
- 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()`
2828
- **`.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
3030

3131
## API & Cross-Environment
3232

3333
- **Routes**: Absolute `/api/{area}/{controller}` + `ApiController` base. Never `[Area]` on APIs (causes 403)
3434
- **Frontend API calls**: Service layer + `useFetch()`, never raw `fetch()` (must unwrap `{ result, success }`)
35-
- **API URL**: `${import.meta.env.VITE_API_URL}` never hardcode `/api/` (TEST uses `/2/` prefix)
35+
- **API URL**: `${import.meta.env.VITE_API_URL}`, never hardcode `/api/` (TEST uses `/2/` prefix)
3636
- **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
3838

3939
## C# Standards
4040

4141
- **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`).
46-
- **Comments**: Sparingly, why-not-what, complex logic only.
47-
- **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.
4846
- **Log injection**: Sanitize user input before logging via `LogSanitizer` (`SanitizeId()`, `SanitizeString()`, `SanitizeYear()`). Skip hard-coded strings, enums, DB values.
4947

5048
## Testing & Git
5149

5250
- **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
5552
- **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.
5653
- **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.
5754
- **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.
5956
- **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

Comments
 (0)