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
The self-nesting outline tree was more structure than chapter outlining needs.
A chapter outline is now a paragraph plus a flat, ordered table of beats, and
tags do the cross-referencing that nesting was doing badly.
A beat is one row: a three-to-five word title, an optional character, what
happened, and what's next. Ordering is a SortOrder column within the chapter —
no parent pointers, no cycle guards, no recursive tree building. Reordering is
one call taking beat ids in the order wanted; ids left out keep their relative
position at the end rather than jumping to the front.
Beats plan, scenes carry prose. The two layers stay separate and a beat's
SceneId is the optional link between them, nullable in both directions —
deleting a scene ungroups its beats rather than deleting the plan, since that
is a decision about prose and not about the outline.
Tags are project-scoped, unique by name case-insensitively, and attach to
characters, chapters and beats through three join tables so cascade deletes are
the database's job rather than ours. Applying an unknown tag by name creates it,
which keeps tagging a single action; GET /api/tags/{id}/references returns
everything carrying a tag across all three kinds at once.
Removed: OutlineNode, OutlineService, its endpoints, agent and MCP tools, and
the Outline tab. Added: Beat and Tag with their services, endpoints, 5 agent
tools and 10 MCP tools, a beat table on the chapter page, a tag editor used in
three places, and a Tags tab for cross-referencing.
Migration drops OutlineNodes — the scaffolder's data-loss warning is the
intended removal, not an accident.
44 tests, up from 31.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S56bfZMGe1hnhpWP4CjjNw
Builds out the vertical slice for planning and writing a novel. Three front
ends — the React UI, an embedded Claude agent, and an MCP stdio server — all go
through one REST API, so an edit made from Claude Code and one made in the
browser are the same edit.
Layout:
Domain entities and enums, no dependencies
Application services, DTOs, the agent tool-use loop and its 15 tools
Infrastructure EF Core 10 + SQLite, Anthropic SDK client
Api ASP.NET Core 10 minimal APIs, OpenAPI, ProblemDetails
Mcp MCP stdio server, 21 tools over the same REST API
Web React 19 + Vite + TanStack Query + Tailwind v4
Data model is Project > Characters / OutlineNodes / Chapters > Scenes, plus
agent conversations. The outline is a self-nesting tree so acts, sequences and
beats can be arranged however the book wants; scenes carry goal/conflict/outcome
because that is what the agent drafts prose from.
Notes on a few choices:
- Conversation history replays to the model as text only. The agent re-reads
current state through its tools rather than trusting a record of edits that
may since have changed in the UI.
- The user's turn is persisted before the tool loop runs, so a question is
recorded even when the model call fails. Turn order uses an explicit sequence
column; timestamps tie when a turn completes inside one tick.
- Tool failures return is_error results rather than throwing, so the model can
read the message and correct itself. MCP tools do the same via CallToolResult,
which keeps the API's own message instead of a generic SDK error.
- The Anthropic client is constructed lazily. It is injected into the agent
service, which also serves read-only endpoints, and those should keep working
on an install with no key. Sending without one returns 503, not 400.
- DateTimeOffset is stored as UTC ticks. SQLite refuses to ORDER BY the default
text form, which every "recently updated first" listing depends on.
Tests run against real in-memory SQLite rather than the EF in-memory provider so
they exercise the cascade deletes and query translation that actually ship.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S56bfZMGe1hnhpWP4CjjNw