Files
novelly/docs/plans/api/outline_import_agent_output.md
James Wampler 189ebf3237 Add outline import feature; drop Dto naming, map entities at the API boundary
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.
2026-08-06 18:36:40 -07:00

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 migration AddImportJobs), 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-scoped list_source_files/read_source_file/read_ledger/write_ledger (the only write capability, enforced at the tool layer, not just the prompt) plus create_project/update_project_brief/create_character/update_character/create_chapter/update_chapter/create_beat/add_arc_stage wrapping 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, a Channel<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/ImportInspection types, useInspectImport/useStartImport/useImportJob (polling, stops on terminal status).
  • components/ImportDialog.tsx: path input → Check → Start/Resume/Delete-and-reimport → progress polling → done. Wired into OverviewPage.tsx (new aside card) and ProjectsPage.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/inspect against the real examples/blade-itself folder (partially imported by the CLI subagent earlier this session) correctly reported Resumable, 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; no ANTHROPIC_API_KEY is configured in this environment, so it terminated as Failed with the expected AgentNotConfiguredException message 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.