Ported both files from wamplerj/mic-check and retargeted them to this project's
stack, then brought the code into line with the rules rather than watering the
rules down to fit the code.
.editorconfig — C# rules carried over verbatim, with four changes:
- Added root = true and a [*] section (utf-8, space indent, final newline,
trim trailing whitespace). Without root the file inherits from any parent
.editorconfig above the checkout.
- end_of_line lf rather than crlf. Every file here is LF and there is no
.gitattributes to normalise on checkout, so crlf would rewrite the tree on
first save.
- csharp_style_namespace_declarations file_scoped, was block_scoped. The source
file sets file_scoped under [*.{cs,vb}] and block_scoped under [*.cs]; the
C#-specific key wins, so the two disagreeing meant C# silently got
block_scoped. Every .cs file here is file-scoped.
- Added sections for the React client (ts/tsx/js 2-space, 100 cols), json/yaml,
css/html, markdown (trailing whitespace preserved — it is a line break there)
and MSBuild files.
Also dropped a duplicated dotnet_naming_style.pascal_case block that appeared
twice verbatim in the source.
CLAUDE.md — same structure and voice, retargeted: React not Vue, xUnit and
FluentAssertions not NUnit and jest, this repo's six projects, and the real
testing approach (in-memory SQLite via TestDatabase, model calls faked at the
IAgentModelClient seam). Added sections the standards did not cover: the
three-front-ends-one-API rule, PATCH semantics, and a note that build-and-tests
green is not the same as working, with the commands to actually run each piece.
Code brought into compliance:
- Removed sealed from five types (the standard says no sealed)
- NovelAgentToolset.ExecuteAsync returned a named tuple; it now returns an
AgentToolResult record (the standard says no tuples for return types)
- Added LangVersion latest to all six csproj files
None of the style rules produce build warnings — the IDE analyzers behind them
are off unless EnforceCodeStyleInBuild is set, and verified they stay silent
with it on too. 44 tests still pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S56bfZMGe1hnhpWP4CjjNw
83 lines
4.6 KiB
Markdown
83 lines
4.6 KiB
Markdown
# CLAUDE.md
|
|
|
|
Guidance for Claude Code (claude.ai/code) in this repo.
|
|
|
|
## Project
|
|
|
|
Novel Software: software for planning and writing a novel. ASP.NET Core 10, C#, TypeScript, React. Chapter outlines, character dossiers, prose drafting, an embedded Claude agent, and an MCP server over the same API.
|
|
|
|
## Structure
|
|
|
|
- `src/NovelSoftware.Domain/` — entities and enums, no dependencies
|
|
- `src/NovelSoftware.Application/` — services, DTOs, the agent tool-use loop
|
|
- `src/NovelSoftware.Infrastructure/` — EF Core + SQLite, Anthropic SDK client
|
|
- `src/NovelSoftware.Api/` — minimal API endpoints
|
|
- `src/NovelSoftware.Mcp/` — MCP stdio server
|
|
- `src/NovelSoftware.Web/` — React + Vite client
|
|
- `tests/` — test suite
|
|
- `docs/` — documentation
|
|
|
|
## Best Practices
|
|
|
|
- Use latest .NET + latest supported nuget packages for that version
|
|
- Set `langVersion` to latest in all csproj files; enable nullable
|
|
- Organize code by feature/area, not type
|
|
- New features need unit tests covering logic as much as possible
|
|
- Modified file: check missing test coverage, all tests pass
|
|
|
|
## Architecture
|
|
|
|
- The React client, the embedded agent, and the MCP server all go through the same REST API. One source of truth — never let a client reach past the API to the database.
|
|
- Agent tools and MCP tools call the same application services the endpoints do. New capability = new service method, then surface it in all three.
|
|
- Domain has no dependencies. Application depends on Domain. Infrastructure depends on Application. Nothing depends on Api.
|
|
|
|
# Coding
|
|
|
|
- Descriptive names all classes/methods. No generic: Provider, Manager, Helper
|
|
- Match formatting/style from `.editorconfig`
|
|
- Wrap lines at 220 chars, single line if fewer
|
|
- Interfaces implemented by single class → bottom of class file. Interface w/ multiple implementations → separate file.
|
|
- No tuples for return types. Prefer records or classes for multiple values
|
|
- Use `record` for data objects, `class` for objects with behavior. Avoid mutable state where possible.
|
|
- DTOs are records; entities are classes
|
|
- `PATCH` requests are partial: null field = leave alone, empty string = clear. Keep new update endpoints consistent with `Patch.Apply`.
|
|
- Enums cross the wire as names, never ordinals
|
|
|
|
## Testing
|
|
|
|
- Min 70% code coverage, target 90%. Unit tests focus end-user scenarios first.
|
|
- Don't write tests just for coverage. Call out missing coverage rather than cover stuff not valuable to end user.
|
|
- Code not cleanly unit-testable → mark `[ExcludeFromCodeCoverage]` or exclude namespace from coverage in .runsettings file
|
|
- BDD-style unit tests, end-to-end as possible. e.g. `Deleting_a_scene_leaves_its_beats_alone`
|
|
- xUnit + FluentAssertions
|
|
- Tests run against real in-memory SQLite via `TestDatabase`, not the EF InMemory provider — cascade deletes and query translation must be exercised, and the InMemory provider fakes both
|
|
- Model calls are faked at the `IAgentModelClient` seam (see `ScriptedModelClient`). Never hit the Anthropic API from a test.
|
|
- No "Mock" in mocked object names
|
|
- No Arrange/Act/Assert comments
|
|
- All tests pass before commit
|
|
|
|
## Verifying
|
|
|
|
Build and tests passing is not the same as working. For anything touching an endpoint, the agent loop, or the MCP server, run it:
|
|
|
|
- API: `ASPNETCORE_URLS=http://localhost:5080 dotnet run --project src/NovelSoftware.Api`, then exercise the route with curl
|
|
- Web: `cd src/NovelSoftware.Web && npm run dev` — proxies `/api` to :5080
|
|
- MCP: build it, then drive it over stdio JSON-RPC (`initialize` → `notifications/initialized` → `tools/list` → `tools/call`)
|
|
|
|
Several real bugs here — SQLite refusing to ORDER BY a DateTimeOffset, the agent's model client throwing at construction and taking read-only endpoints down with it — passed the build and the test suite and only showed up when the app actually ran.
|
|
|
|
## Claude
|
|
|
|
- Plans = `.md` files in `docs/plans/`. Web → `docs/plans/web/`, API → `docs/plans/api/`
|
|
- Split large plans into discrete chunks — each buildable + committable independently
|
|
- Plan generated from `docs/plans/<name>.md` → save as `docs/plans/<name>_plan.md`
|
|
- Plan implemented from `docs/plans/<name>.md` → save summary as `docs/plans/<name>_output.md`
|
|
|
|
## Stack
|
|
|
|
`.gitignore` set for .NET/Visual Studio (C#, NuGet, MSBuild) plus Node. Update if stack change.
|
|
|
|
- Anthropic model id lives in `appsettings.json` under `Agent:Model`. Don't hardcode it.
|
|
- API key comes from `ANTHROPIC_API_KEY` or `Agent:ApiKey` — never commit one. The app must stay fully usable without a key; only the agent endpoints require it.
|
|
- EF migrations: `dotnet ef migrations add <Name> -p src/NovelSoftware.Infrastructure -s src/NovelSoftware.Api -o Persistence/Migrations`. The API migrates on boot.
|