Sandbox source paths under a configured Imports:RootPath, browse it
from the web dialog, upload a zip that extracts into staging, and
import a single markdown file (agent infers chapter vs character).
Renames the domain concept from Project to Novel throughout the backend
(entities, DTOs, services, endpoints, ProjectAccessService/Permission,
ProjectId foreign keys), MCP server (tool names and routes), and the
React/Vite frontend (types, hooks, routes, components). Adds a new EF
Core migration (RenameProjectToNovel) using RenameTable/RenameColumn to
preserve existing data instead of dropping/recreating tables. Updates
CLAUDE.md's structure section to reference Novels/ instead of Projects/.
- Characters can carry aliases and be linked as the same underlying
person (canonical SameCharacterAsId, optional reveal chapter/note),
surfaced through the API, MCP tools, agent toolset, and web UI.
- Characters page redesigned as a filterable/sortable table (name+
aliases, role, importance, occupation, tags) instead of a sidebar
list, to stay usable as the cast grows.
- Beats can be moved between chapters (BeatService.MoveAsync + MCP/
agent tool + endpoint).
- Add a keyboard-shortcuts help overlay (HelpButton/HelpOverlayContext)
wired into the project layout.
- CLAUDE.md: require every frontend component to carry a unique id
attribute; apply it to CharacterMultiSelect and MarkdownEditor.
Drop the Scene entity/grouping in favor of chapters carrying prose directly
and beats belonging to many characters. Add markdown editor + character
multi-select components to the web client. Remove all XML doc and inline
comments across the touched C#/TS/CSS files in favor of self-documenting
names, and record that convention in CLAUDE.md. Add .mcp.json (local MCP
server config, no secrets) and ignore .idea/.
Information at endpoint and service-method boundaries, Debug in deeper
helpers, Warning before expected/recoverable failures (not-found,
validation, agent tool errors), Error on caught exceptions. Serilog
wraps the exception handler so request-completion logs report the
resolved status code rather than the raw exception. Never logs prose
bodies or the Anthropic API key.
The layered split into Domain/Application/Infrastructure/Api was forcing
organisation by layer: adding one capability meant touching four projects and
four folders that each held a slice of it. Those four projects are now one
feature-organised Novelly.Api, where each folder — Projects, Characters,
Chapters, Beats, Scenes, Tags, Agent — holds its entity, DTOs, service and
endpoints together. Common/ holds what genuinely crosses features (the patch
semantics, the two exception types, DraftStatus) and Data/ holds the DbContext
and migrations.
Six .NET projects become five: the three layer projects are gone, and
Novelly.AppHost and Novelly.ServiceDefaults are new.
- Namespaces move from NovelSoftware.* to Novelly.*, including the entity type
names recorded in the EF model snapshots. The migration ids are untouched, so
an existing novel.db still migrates cleanly — verified against a fresh file.
- Aspire orchestration mirrors the mic-check setup: the AppHost starts the API
on :5080 and the Vite dev server on :5173, and the API picks up OpenTelemetry,
health checks and service discovery from ServiceDefaults. /health and /alive
now answer in development.
- A Husky pre-push hook runs scripts/ci/prepush.sh: build, test, then a web
build. The scripts are plain bash so CI can run the same steps.
- The MCP server's env var is now NOVELLY_API_URL.
Verified beyond the build: 44 tests pass, the web client builds, the API was
exercised over curl (project/chapter/beat/tag round trip, tag cross-reference,
503 on the agent without a key while conversation listing still returns 200),
the MCP server was driven over stdio JSON-RPC (26 tools, errors still surface
the API's own message rather than being flattened), and the AppHost was run to
confirm both resources come up and Vite proxies /api through to the API.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S56bfZMGe1hnhpWP4CjjNw
Replaces xUnit + FluentAssertions with NUnit, asserting through Assert.That and
the constraint model. Adds concrete examples for the shapes that come up most
here — collection equality, null checks, expected exceptions — plus a note on
Assert.Multiple, since a plain chain stops at the first failure and hides the
rest of a broken case.
Documentation only. The existing 44 tests are still xUnit + FluentAssertions and
now predate the standard; migrating them is a separate change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S56bfZMGe1hnhpWP4CjjNw
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