diff --git a/.claude/rules/archetype/library.md b/.claude/rules/archetype/library.md new file mode 100644 index 0000000..11cf7fd --- /dev/null +++ b/.claude/rules/archetype/library.md @@ -0,0 +1,20 @@ +# Archetype — Library (shipped, reusable package) + +For code consumed by other projects (NuGet packages, shared libraries). +Compose with the five core rules. + +Structure (deltas from `core/architecture.md`) +- Unit tests under `test/.UnitTests/`; integration tests that need + a real dependency under `test/.IntegrationTests/`. + +Packaging & compatibility +- `dotnet pack` runs in CI from day one so packaging bugs surface early. +- Target the full supported TFM matrix (e.g. `net8.0;net10.0`); CI runs the + matrix on every OS you claim to support. +- The public API is a contract: follow semantic versioning, keep XML docs + complete (they ship in the package), and treat any breaking change as a + deliberate, documented major bump. + +Testing +- Cover the public surface directly; internal helpers are tested through it, + not around it. diff --git a/.claude/rules/architecture.md b/.claude/rules/architecture.md deleted file mode 100644 index 1b87216..0000000 --- a/.claude/rules/architecture.md +++ /dev/null @@ -1,8 +0,0 @@ -# Architecture - -Rules governing physical project and solution structure (as opposed to logical design, which lives in `design-principles.md`): - -- Repo/solution layout: source under `src//`, unit tests under `test/.UnitTests/` (singular `test`), integration tests requiring a real dependency (e.g. a database provider) under `test/.IntegrationTests/`, benchmark projects (BenchmarkDotNet) under `bench//`, one solution file (`.slnx`) per repo or example, at its root, referencing every project beneath it. -- Benchmark projects join the solution so CI builds them, but CI never runs them — benchmark numbers from shared runners are noise; baselines are produced locally and documented in `docs/`. -- Centralize shared MSBuild properties (`TargetFramework`, `Nullable`, `TreatWarningsAsErrors`, etc.) in a `Directory.Build.props` at that same root instead of repeating them per `.csproj`. -- `dotnet pack` runs in CI from day one so packaging bugs surface early. diff --git a/.claude/rules/coding-standards.md b/.claude/rules/coding-standards.md deleted file mode 100644 index 53ac9e8..0000000 --- a/.claude/rules/coding-standards.md +++ /dev/null @@ -1,13 +0,0 @@ -# Coding Standards - -Always follow the Microsoft C# coding conventions and .NET naming guidelines: - -- PascalCase for types, methods, properties, constants, and public members; camelCase for locals and parameters; `_camelCase` for private fields (`private static readonly` values are constant-like and stay PascalCase); `I` prefix for interfaces; `Async` suffix for async methods. -- One top-level type per file; file name matches the type name. Exception: a small supporting type (typically a record or struct) that exists only to serve the file's primary type — a companion DTO, result, or value type it owns and produces — may be declared in the same file. The exception covers helpers bound to that one type; it does not license unrelated types or a type intended for reuse across the codebase, which still get their own file. -- Use file-scoped namespaces, `var` when the type is apparent, expression-bodied members only when they improve readability. -- Enable and respect nullable reference types (`enable`); never suppress warnings with `!` without a comment justifying it. -- Prefer records for immutable data, `readonly` where possible, and pattern matching over type checks/casts. Exception: ORM-mapped entities that require mutable, parameterless-constructible state (e.g., EF Core) may remain classes. -- Public APIs must have XML doc comments; internal code is documented only where intent is not obvious from the code. Test projects are exempt — their `public` types exist only for test-framework discovery, not as a consumed API surface. -- Never include unnecessary using directives. -- Codify these conventions in an `.editorconfig` at the solution root (naming rules, `file_scoped` namespaces, `IDE0005` for unused usings, etc.) so they are tool-enforced rather than prose-only, and set `true` so style violations fail the build alongside `TreatWarningsAsErrors`. -- All code must pass `dotnet format` and build with zero warnings (`TreatWarningsAsErrors` is on). diff --git a/.claude/rules/core/architecture.md b/.claude/rules/core/architecture.md new file mode 100644 index 0000000..a75e139 --- /dev/null +++ b/.claude/rules/core/architecture.md @@ -0,0 +1,13 @@ +# Architecture + +Rules governing physical project and solution structure (as opposed to logical +design, which lives in `design-principles.md`). This is the invariant skeleton; +archetype files add the parts that vary — test-project naming, `bench/`, +integration-test projects, packaging. + +- Source lives under `src//`; tests under `test/` (singular). +- One solution file (`.slnx`) per repo, at its root, referencing every project + beneath it. +- Centralize shared MSBuild properties (`TargetFramework`, `Nullable`, + `TreatWarningsAsErrors`, etc.) in a `Directory.Build.props` at that same root + instead of repeating them per `.csproj`. diff --git a/.claude/rules/core/coding-standards.md b/.claude/rules/core/coding-standards.md new file mode 100644 index 0000000..819a510 --- /dev/null +++ b/.claude/rules/core/coding-standards.md @@ -0,0 +1,31 @@ +# Coding Standards + +Always follow the Microsoft C# coding conventions and .NET naming guidelines: + +- PascalCase for types, methods, properties, constants, and public members; + camelCase for locals and parameters; `_camelCase` for private instance + fields (`private static readonly` values are constant-like and stay + PascalCase); `I` prefix for interfaces; `Async` suffix for async methods. +- One top-level type per file; file name matches the type name. Exception: a + small supporting type (typically a record or struct) that exists only to + serve the file's primary type — a companion DTO, result, or value type it + owns and produces — may be declared in the same file. The exception covers + helpers bound to that one type; it does not license unrelated types or a type + intended for reuse across the codebase, which still get their own file. +- Use file-scoped namespaces, `var` when the type is apparent, expression-bodied + members only when they improve readability. +- Enable and respect nullable reference types (`enable`); + never suppress warnings with `!` without a comment justifying it. +- Prefer records for immutable data, `readonly` where possible, and pattern + matching over type checks/casts. +- Public APIs must have XML doc comments; internal code is documented only where + intent is not obvious from the code. Test projects are exempt — their `public` + types exist for test-framework discovery, not as a consumed API surface. +- Never include unnecessary using directives. +- Codify these conventions in an `.editorconfig` at the solution root (naming + rules, `file_scoped` namespaces, `IDE0005` for unused usings, etc.) so they + are tool-enforced rather than prose-only, and set + `true` so style violations + fail the build alongside `TreatWarningsAsErrors`. +- All code must pass `dotnet format` and build with zero warnings + (`TreatWarningsAsErrors` is on). diff --git a/.claude/rules/design-principles.md b/.claude/rules/core/design-principles.md similarity index 98% rename from .claude/rules/design-principles.md rename to .claude/rules/core/design-principles.md index e424425..7174b56 100644 --- a/.claude/rules/design-principles.md +++ b/.claude/rules/core/design-principles.md @@ -1,4 +1,4 @@ -# Design Principles +# Design Principles - Always apply well-established object-oriented design practices: favor composition over inheritance, encapsulate invariants inside domain types, keep classes small and cohesive, and depend on abstractions at layer boundaries. - Adhere to SOLID to keep the code adaptive to change: diff --git a/.claude/rules/core/testing-philosophy.md b/.claude/rules/core/testing-philosophy.md new file mode 100644 index 0000000..219e50b --- /dev/null +++ b/.claude/rules/core/testing-philosophy.md @@ -0,0 +1,28 @@ +# Testing — Test-Driven Development + +YOU MUST follow a test-driven development approach for all production code: + +1. **Red** — write a failing test that specifies the desired behavior before + writing implementation code. +2. **Green** — write the minimum implementation to make the test pass. +3. **Refactor** — clean up the code and tests while keeping everything green. + +Rules: + +- Never write production code without a failing test that motivates it. Never + mark a task complete while any test fails. Exception: when porting or adapting + already-designed code (e.g., a pre-written draft) rather than discovering new + behavior, there is no red step — treat `dotnet test` passing as the gate. +- Ensure all relevant edge cases are covered: null/empty inputs, boundary + values, invalid state transitions, concurrency where applicable, and failure + paths (exceptions, timeouts, cancellation via `CancellationToken`). +- When a task explicitly scopes test coverage (e.g., "one test", "happy path + only"), that explicit scope takes precedence over this checklist — but say so + out loud (commit message, notes, or conversation) rather than silently + under-testing. +- Test naming: `Should_ExpectedOutcome_When_Scenario` (e.g., + `Should_ThrowInsufficientStockException_When_InventoryInsufficient`). +- Structure tests as Arrange–Act–Assert. +- Unit tests must be fast and deterministic: no real I/O, network, clock, or + `Task.Delay`. Abstract time behind `TimeProvider`. +- Run `dotnet test` after every change and before declaring any work finished. diff --git a/.claude/rules/core/workflow-core.md b/.claude/rules/core/workflow-core.md new file mode 100644 index 0000000..0281b86 --- /dev/null +++ b/.claude/rules/core/workflow-core.md @@ -0,0 +1,26 @@ +# Workflow — Core + +Applies to every project regardless of team size or archetype. Compose with +exactly one of `overlays/workflow-solo.md` or `overlays/workflow-team.md`. + +- Make minimal, focused changes; do not refactor unrelated code in the same + change. +- One logical change per commit, with an imperative-mood message explaining + *why*. +- In anything the forge renders — commit messages, PR titles/descriptions, and + review comments — write `#N` (or an issue/PR URL) only for an item that + actually exists and is being deliberately referenced; verify with + `gh issue view N` / `gh pr view N` when in doubt. Every other number- or + token-like reference — step numbers, analyzer IDs, external ticket keys, + version numbers — is wrapped in backticks as inline code, so autolinking + never fabricates a link and closing keywords (`Fixes #N`) never fire against + the wrong item. +- When unsure between two designs, present both with trade-offs and ask before + implementing. +- Never commit or push directly to the default branch (`master`/`main`). All + work happens on a `feature/`-prefixed branch. +- Merge by squash so each feature arrives as a single logical commit on the + default branch, consistent with the "one logical change per commit" rule + above; the squash commit message keeps the imperative, *why*-focused form. +- Update the applicable workflow rule file when a new convention or correction + is established. diff --git a/.claude/rules/overlays/benchmarks.md b/.claude/rules/overlays/benchmarks.md new file mode 100644 index 0000000..b1990dd --- /dev/null +++ b/.claude/rules/overlays/benchmarks.md @@ -0,0 +1,10 @@ +# Overlay — Benchmarks (BenchmarkDotNet) + +Add when the project tracks performance. + +- Benchmark projects live under `bench//` and join the solution so + CI *builds* them. +- CI never *runs* benchmarks — numbers from shared runners are noise. Produce + baselines locally and record them in `docs/`. +- Smoke-test a benchmark's plumbing with `--job Dry` + (e.g. `dotnet run -c Release --project bench/ -- --job Dry`). diff --git a/.claude/rules/overlays/workflow-team.md b/.claude/rules/overlays/workflow-team.md new file mode 100644 index 0000000..db36f22 --- /dev/null +++ b/.claude/rules/overlays/workflow-team.md @@ -0,0 +1,8 @@ +# Workflow — Team project (PR required) + +Compose with `core/workflow-core.md`. Use on any multi-contributor repo. + +- The branch lands on the default branch only through a reviewed pull request. +- Enforce with branch protection: require a PR, disallow direct pushes, require + the squash-merge strategy — so it can't be bypassed by accident. +- Never open a pull request automatically — confirm with the maintainer first. diff --git a/.claude/rules/profiles/library-team.md b/.claude/rules/profiles/library-team.md new file mode 100644 index 0000000..24c1a7d --- /dev/null +++ b/.claude/rules/profiles/library-team.md @@ -0,0 +1,9 @@ +# Profile: library-team — reusable package, multiple contributors +@../core/coding-standards.md +@../core/design-principles.md +@../core/architecture.md +@../core/testing-philosophy.md +@../core/workflow-core.md +@../overlays/workflow-team.md +@../archetype/library.md +@../overlays/benchmarks.md diff --git a/.claude/rules/project/sqlbound.md b/.claude/rules/project/sqlbound.md new file mode 100644 index 0000000..ad02e32 --- /dev/null +++ b/.claude/rules/project/sqlbound.md @@ -0,0 +1,53 @@ +# SqlBound — project-specific rules + +Rules that apply only to this repository, composed on top of the shared +`library-team` profile. Shared modules under `core/`, `archetype/`, and +`overlays/`, plus the profile manifest under `profiles/`, are copied +verbatim from the `claude-rules` repo and must stay byte-identical so +`tools/sync.ps1 -Check` audits cleanly — never edit them here. +Sqlbound-specific conventions land in this file; corrections to a shared +rule go upstream to `claude-rules` first and are then re-synced. + +## Workflow — milestones, phases, versioning + +- For each milestone, draft a plan first and present it to the user; execution + starts only after the user approves the plan. +- Milestones (M1–M16) are used to track the progress of the project. All + milestone work happens on a `feature/M-`-prefixed branch. +- Versioning follows semantic versioning: each phase gets its own version line + (Phase 1 → `0.1.x`, Phase 2 → `0.2.x`, …; Phase 6 ships `1.0.0`). +- Tag the default branch with an annotated tag (e.g., + `git tag -a v0.1.0 -m "..."`) when a phase completes and for each release + candidate cut along the way, then push the tag to GitHub for reference. +- `PackageVersion` in `Directory.Build.props` carries a prerelease suffix + while a phase is in flight: `X.Y.Z-preview.N` during development, and + `X.Y.Z-rc.N` once the phase is tracking a stable release. Closing the phase + drops the suffix to the clean `X.Y.Z` in the same commit that gets tagged. + Every tag — release candidates included — is cut on a commit whose + `PackageVersion` matches the tag name minus the leading `v`, so a tag always + names its package version exactly. The next phase opens at the version the + roadmap assigns it, not at a mechanical bump of the previous minor. +- A release candidate does not close its phase: `PackageVersion` moves to + `X.Y.Z-rc.N`, `CHANGELOG.md` gets its own `## [X.Y.Z-rc.N]` entry, and the + annotated tag records that the candidate is not published to nuget.org. + Further candidates bump `N`; the clean `X.Y.Z` and its `vX.Y.Z` tag at GA + are what close the phase and its milestone. +- Each phase has one matching GitHub milestone (titled + `Phase N — (..x)`), not one per M-number; every + M-number's PR in that phase is associated with the phase's milestone on + creation, and the milestone is closed when the phase's final PR merges. The + milestone's description lists each composing M-number with its own + description as a bullet, so the phase-level summary and the per-milestone + detail both stay visible in one place. + +## Testing + +- Generator output is snapshot-tested with the Roslyn testing SDK. +- Benchmarks compare against Dapper and raw ADO.NET (the `bench/` layout and + CI policy come from the shared benchmarks overlay). + +## Architecture + +- The shared "one solution file (`.slnx`) per repo, at its root" rule extends + here to "per repo or example": a self-contained example project gets its own + `.slnx` at its own root. diff --git a/.claude/rules/testing.md b/.claude/rules/testing.md deleted file mode 100644 index 2482724..0000000 --- a/.claude/rules/testing.md +++ /dev/null @@ -1,19 +0,0 @@ -# Testing — Test-Driven Development - -YOU MUST follow a test-driven development approach for all production code: - -1. **Red** — write a failing test that specifies the desired behavior before writing implementation code. -2. **Green** — write the minimum implementation to make the test pass. -3. **Refactor** — clean up the code and tests while keeping everything green. - -Rules: - -- Never write production code without a failing test that motivates it. Never mark a task complete while any test fails. -- Ensure all relevant edge cases are covered: null/empty inputs, boundary values, invalid state transitions, concurrency where applicable, and failure paths (exceptions, timeouts, cancellation via `CancellationToken`). -- When a task explicitly scopes test coverage (e.g., "one test", "happy path only"), that explicit scope takes precedence over this checklist — but say so out loud (commit message, notes, or conversation) rather than silently under-testing. -- Test naming: `Should_ExpectedOutcome_When_Scenario` (e.g., `Should_ThrowInsufficientStockException_When_InventoryInsufficient`). -- Structure tests as Arrange–Act–Assert. -- Unit tests must be fast and deterministic: no real I/O, network, clock, or `Task.Delay`. Abstract time behind `TimeProvider`. -- Run `dotnet test` after every change and before declaring any work finished. -- Generator output is snapshot-tested with the Roslyn testing SDK. -- Benchmarks (BenchmarkDotNet, vs Dapper and raw ADO.NET) live in the repo. diff --git a/.claude/rules/workflow.md b/.claude/rules/workflow.md deleted file mode 100644 index 2f18348..0000000 --- a/.claude/rules/workflow.md +++ /dev/null @@ -1,15 +0,0 @@ -# Workflow - -- For each milestone, draft a plan first and present it to the user; execution starts only after the user approves the plan. -- Never open a pull request automatically — always confirm with the user first. -- Make minimal, focused changes; do not refactor unrelated code in the same change. -- One logical change per commit, with an imperative-mood message explaining *why*. -- When unsure between two designs, present both with trade-offs and ask before implementing. -- Never commit or push directly to the default branch (`master`/`main`). Milestones are used to track the progress of the project. All work happens on a `feature/M-`-prefixed branch. -- The branch lands on the default branch only through a reviewed pull request. Enforce this with branch protection (require a PR, disallow direct pushes, require the squash-merge strategy) so it can't be bypassed by accident. -- Merge by squash so each feature arrives as a single logical commit on the default branch, consistent with the "one logical change per commit" rule above; the squash commit message keeps the imperative, *why*-focused form. -- Versioning follows semantic versioning: each phase gets its own minor version (Phase 1 → `0.1.x`, Phase 2 → `0.2.x`, …; Phase 6 ships `1.0.0`). -- When a phase completes, tag it on the default branch with an annotated tag (e.g., `git tag -a v0.1.0 -m "..."`) and push the tag to GitHub for reference. -- `PackageVersion` carries a prerelease suffix (`X.Y.0-preview.N`) during a phase's active development. Closing the phase drops the suffix to the clean `X.Y.0` in the same commit that gets tagged, so the tag always matches the package version it marks exactly. The next phase's first commit starts the new prerelease line (`X.(Y+1).0-preview.1`). -- Each phase has one matching GitHub milestone (titled `Phase N — (0.Y.x)`), not one per M-number; every M-number's PR in that phase is associated with the phase's milestone on creation, and the milestone is closed when the phase's final PR merges. The milestone's description lists each composing M-number with its own description as a bullet, so the phase-level summary and the per-milestone detail both stay visible in one place. -- Update this file when a new convention or correction is established. diff --git a/CLAUDE.md b/CLAUDE.md index 2343335..1115d7b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -36,8 +36,10 @@ Follow the design constraints below; they are deliberate decisions, not suggesti Work proceeds milestone by milestone (M1–M16) across six phases: Bedrock (skeleton/CI, execution core, Dapper-coexistence sample), Codegen (materialization, query shapes, AOT + benchmarks), Verification (SQL Server introspection, diagnostics, offline snapshots), Providers, Migrations & CLI, and Ship (API freeze, NuGet 1.0). The Dapper-coexistence sample project (M3) doubles as a permanent CI regression test. Codegen precedes verification because the generator defines the shapes the verifier checks. ## Conventions -@.claude/rules/coding-standards.md -@.claude/rules/architecture.md -@.claude/rules/design-principles.md -@.claude/rules/testing.md -@.claude/rules/workflow.md + +Shared rules come from the `library-team` profile of the `claude-rules` repo, +copied in with its `tools/sync.ps1` (audit drift with `-Check`); sqlbound-only +conventions live in the project file. + +@.claude/rules/profiles/library-team.md +@.claude/rules/project/sqlbound.md