Services now return entities; endpoints (and the agent toolsets) map to *Response records instead of services building wire DTOs themselves. Also brings in the outline-import agent, MCP tool, ledger and web dialog that were already in progress on disk.
3.3 KiB
3.3 KiB
Web-triggered outline import — implementation summary
Implements the plan in outline_import_agent_plan.md.
Backend (src/Novelly.Api/Imports/)
ImportJob(+ EF migrationAddImportJobs),ImportPaths(ledger read/write, root-containment path resolution, chapter-file counting — shared by the service and the toolset),ImportDtos,ImportService,ImportEndpoints(POST /api/imports/inspect,POST /api/imports,GET /api/imports/{id}).ImportAgentToolset— root-scopedlist_source_files/read_source_file/read_ledger/write_ledger(the only write capability, enforced at the tool layer, not just the prompt) pluscreate_project/update_project_brief/create_character/update_character/create_chapter/update_chapter/create_beat/add_arc_stagewrapping the same application services the REST API and chat agent use.ImportAgentService— system prompt ported from.claude/agents/outline-importer.md's passes; runs bounded turns (AgentOptions.ImportMaxIterationsPerTurn= 40 tool calls/turn,ImportMaxTurns= 8 turns), re-reading the ledger after each turn as ground truth for "done" rather than trusting the model.ImportJobRunner : BackgroundService— the app's first background-job infra, aChannel<Guid>-backed queue drained in its own DI scope per job.
Wired into NovellyServiceRegistration/Program.cs. All 98 backend tests pass, including 14 new ones (ImportServiceTests, ImportAgentToolsetTests) covering inspect states, job dedup, force-restart's project+ledger deletion, path-traversal rejection, and the ledger-only write scope.
Frontend (src/Novelly.Web)
api/types.ts/api/hooks.ts:ImportJob/ImportInspectiontypes,useInspectImport/useStartImport/useImportJob(polling, stops on terminal status).components/ImportDialog.tsx: path input → Check → Start/Resume/Delete-and-reimport → progress polling → done. Wired intoOverviewPage.tsx(new aside card) andProjectsPage.tsx(button next to "New novel").
Scope note: import always creates its own project (it never populates the project you're already viewing) — the Overview card's copy says so explicitly and navigates to the new project on completion, since threading an "import into this existing project" mode through the toolset/ledger format wasn't part of the approved plan.
Verified live (not just tests)
- Restarted the Aspire AppHost to pick up the migration + new code.
POST /api/imports/inspectagainst the realexamples/blade-itselffolder (partially imported by the CLI subagent earlier this session) correctly reportedResumable, 5/46 chapters,["project","characters"]passes — matches the CLI's own ledger exactly, confirming ledger-format compatibility between the two entry points.POST /api/imports+ polling against a synthetic one-chapter outline exercised the full endpoint → queue → background-job → status-transition path for real over HTTP; noANTHROPIC_API_KEYis configured in this environment, so it terminated asFailedwith the expectedAgentNotConfiguredExceptionmessage rather than a real import — this is the correct behavior for an unconfigured key, but it means the model-driven happy path (actually calling Claude and writing chapters/characters) has not been verified live. That needs a configured key and is worth a manual pass before considering this done-done.